一、引言

数据可视化是移动应用中最具挑战性的前端任务之一。与传统的文字和列表不同,图表需要在有限的空间内同时传递"数值大小"、“变化趋势”、"对比关系"和"分布特征"四层信息。在 HarmonyOS NEXT 中,ArkUI 提供了一系列数据可视化组件——从线性进度条(Progress)到环形仪表盘(Gauge),再到今天我们要深入探讨的数据面板(DataPanel)

DataPanel 是 ArkUI 中专门用于展示多维度数据对比的组件。它的核心形态有两种:折线图模式(DataPanelType.Line)和环形图模式(DataPanelType.Circle)。折线图模式下,它将多个数值点连接成一条折线,直观展示数据的起伏变化;环形图模式下,它用填充面积的大小来对比各个维度的值。无论是运动健康 App 中的周步数统计、财务 App 中的月度收支对比,还是学习 App 中的各科成绩分布,DataPanel 都能以紧凑而优雅的方式呈现场数据。

本文将通过一个完整的**“运动数据看板”**实战案例,深入解析 DataPanel 组件的构造参数(values、max、min)、两种图表类型(Line 与 Circle)、valueColors 色彩映射、trackBackgroundColor 轨道背景色和 strokeWidth 线宽控制。阅读完本文,你将能够:

  • 掌握 DataPanel 的核心构造参数和数据绑定模式
  • 理解 Line 和 Circle 两种类型的视觉差异和适用场景
  • 使用 valueColors() 为每个数据点分配独立颜色
  • 实现数据类型切换和数据集切换的双重交互
  • 构建完整的数据看板页面(DataPanel + 周标签 + 指标选择器)

二、DataPanel 核心 API 详解

2.1 构造函数:values / max / min / type

DataPanel 的构造函数接受四个参数:

DataPanel({
  values: number[],           // 必填:数据值数组,每个元素对应一个维度
  max?: number,               // 可选:最大值,默认 100
  min?: number,               // 可选:最小值,默认 0
  type?: DataPanelType        // 可选:图表类型,Line 或 Circle
})

这四个参数定义了数据面板的"数据域"和"视觉形态"。values 是最核心的参数——它是一个 number[] 数组,数组中的每个元素代表一个维度的数值。例如,在我们的运动数据 Demo 中:

values: [8200, 10500, 6700, 12300, 9100, 14800, 5200]

这 7 个数值分别代表周一至周日的每日步数。DataPanel 会在图表上为每个数值创建一个数据点,并根据 type 参数决定如何渲染它们。

max 定义了图表的"天花板"——所有数值都相对于 max 计算其视觉高度。在我们的步数数据中,我们设置 max: 20000,这意味着 10000 步会在图表中占据 50% 的高度。max 的选择影响图表的视觉张力和可读性:设置得太高,所有柱形都会"缩"在底部,图表显得空洞;设置得太低,高峰值会"顶破"天花板,无法展示完整信息。一个好的经验法则是:max 应该比实际数据中的最大值略高 20%-50%。

min 定义了图表的"地板",默认值为 0。对于绝大部分场景,0 是合理的起点。但在某些场景下你可能想设置非零的 min——比如展示体温变化(35°C-42°C),将 min 设为 35 可以让 0.1°C 的变化在图中有更明显的视觉呈现。

typeDataPanelType 枚举,有两个值:

  • DataPanelType.Line:折线图模式。每个数据点用圆点标记,相邻数据点之间用直线连接,形成一条完整的折线。适合展示"趋势"——数据如何随时间变化、何时上升、何时下降。

  • DataPanelType.Circle:环形图模式(也称雷达图/蛛网图)。每个维度的数值映射为从中心向外辐射的"辐条"长度,各辐条端点依次连接形成一个封闭的多边形。适合展示"对比"——哪些维度突出、哪些维度薄弱、整体是否平衡。

在我们的 Demo 中,我们通过一个切换按钮让用户在两种类型之间自由切换:

const CHART_TYPES: Array<{ label: string; value: DataPanelType }> = [
  { label: '折线', value: DataPanelType.Line },
  { label: '环形', value: DataPanelType.Circle },
];

2.2 valueColors:每个数据点的颜色

valueColors() 接受一个 ResourceColor[] 数组,为每个数据点指定独立颜色:

.valueColors(['#1677FF', '#4096FF', '#69B1FF', '#91CAFF', '#BAE0FF', '#D6E4FF', '#E6F0FF'])

这是一个非常灵活的设计。你可以使用:

  • 单一色系渐变(如我们的步数数据):从深蓝到浅蓝,数据点颜色逐渐变浅,营造一种"由近及远"的时间感。
  • 冷暖对比(如卡路里数据):从深红到浅粉,暗示"热量"的视觉温度。
  • 自然色系(如运动时长数据):从深绿到浅绿,暗示"健康"和"活力"。

在我们的 Demo 中,三种数据集各自使用不同的颜色系,切换数据集时整个 DataPanel 的视觉风格也随之改变。这不是简单的"换个颜色"——而是让颜色成为数据语义的一部分。蓝色系让用户联想到"步数=运动=活力",红色系让用户联想到"卡路里=热量=消耗",绿色系让用户联想到"时长=健康=自然"。

需要注意的是,valueColors 数组的长度应当与 values 数组的长度一致。如果 valueColors 数组比 values 短,多余的数据点将使用默认颜色(通常是组件的主题色)。

2.3 trackBackgroundColor:轨道的"底色"

.trackBackgroundColor('#FFFFFF22')

trackBackgroundColor 设置图表"轨道"(即背景网格或参考线)的颜色。在我们的 Demo 中,因为 DataPanel 所在的卡片背景是深色的(#1a1a2e),我们使用了白色半透明(#FFFFFF22,即白色 13% 透明度)作为轨道背景色,使得网格线在深色背景上隐约可见,提供参照但不喧宾夺主。

在浅色背景的卡片上,通常使用 '#F0F0F5''#E8E8ED' 作为轨道背景色,营造一种淡淡的参照网格。

2.4 strokeWidth:线条的粗细

.strokeWidth(2)

strokeWidth 控制折线的粗细(Line 模式下)或环形边框的粗细(Circle 模式下)。在 Line 模式下,strokeWidth(2) 表示 2vp 粗细的折线,精致而不失清晰。如果设为 4 或 6,折线会显得更"粗壮有力",适合强调趋势变化的场景。如果设为 1,折线会非常纤细,适合数据点密集、需要精确读数的场景。

2.5 数据响应式更新

DataPanel 的数据更新遵循 ArkUI 声明式 UI 的标准模式。当 @State 变量(如 this.values)发生变化时,DataPanel 会自动重新渲染:

// 切换数据集时
this.dataSetIndex = idx;  // 触发 getCurrentDataSet() 返回新的数据集
// DataPanel 的 values、valueColors 全部自动更新

不需要手动调用任何"刷新"或"重绘"方法——这就是声明式框架的核心优势。你需要关心的只是"数据是什么",而不是"如何更新视图"。
在这里插入图片描述

三、实战:运动数据看板

3.1 页面整体设计

我们的运动数据看板围绕"运动健康"的场景设计。页面分为三个区域:

  1. 顶部标题栏:深色背景(#1a1a2e),展示"📈 运动数据"
  2. 数据面板区:同样深色背景,包含 DataPanel 图表 + 周标签行
  3. 控制区:白色背景,包含图表类型切换 + 数据指标选择

三个区域用不同的背景色区分,形成清晰的信息层次。

3.2 三种运动数据集

我们设计了三种运动数据集,每种包含 7 天的数据(周一至周日):

const DATA_SETS: DataSet[] = [
  {
    label: '每日步数', icon: '🚶', unit: '步',
    values: [8200, 10500, 6700, 12300, 9100, 14800, 5200],
    colors: ['#1677FF', '#4096FF', '#69B1FF', '#91CAFF', '#BAE0FF', '#D6E4FF', '#E6F0FF']
  },
  {
    label: '消耗卡路里', icon: '🔥', unit: 'kcal',
    values: [320, 450, 280, 510, 380, 620, 210],
    colors: ['#FF4D4F', '#FF6B6B', '#FF8C8C', '#FFADAD', '#FFC7C7', '#FFD9D9', '#FFE8E8']
  },
  {
    label: '运动时长', icon: '⏱️', unit: '分钟',
    values: [45, 60, 30, 75, 50, 90, 25],
    colors: ['#52C41A', '#73D13D', '#95DE64', '#B7EB8F', '#D9F2BE', '#E8F7D9', '#F0FAEB']
  },
];

每种数据集的 values 数组遵循相同的"周模式"——周一到周三逐渐上升,周三到周五波动,周六达到峰值(运动最多的一天),周日回落到最低点。这个模式符合真实用户的运动习惯:工作日保持中等运动量,周末有一天大量运动,另一天则休息恢复。

3.3 周标签行:数值的"字面解读"

DataPanel 下方的周标签行展示了每个维度的"字面值":

Row() {
  ForEach(DAY_LABELS, (day: string, idx: number) => {
    Column() {
      Text(day)                                        // "周一"
        .fontSize(10).fontColor('#FFFFFF88')
      Text(`${this.getCurrentDataSet().values[idx]}`)  // "8200"
        .fontSize(11).fontColor('#FFFFFF')
        .fontWeight(FontWeight.Medium)
        .fontFamily('monospace')
    }
    .layoutWeight(1)
    .alignItems(HorizontalAlign.Center)
  })
}

这里有一个微妙的细节:数值使用了 monospace 等宽字体族。等宽字体中每个数字的宽度相同,所以当用户切换数据集(比如从 8200 步切到 320 kcal),数字虽然变化了,但不会因为字符宽度不同而导致标签列的宽度"抖动"。这是一个常见但容易被忽略的 UI 细节——在数据频繁切换的场景中,布局的稳定性直接影响用户的阅读体验。

3.4 图表类型切换:Line vs Circle

图表类型切换使用两个互斥的 pill 按钮:

Row() {
  ForEach(CHART_TYPES, (opt: ChartTypeOption, idx: number) => {
    Text(opt.label)
      .fontColor(this.chartTypeIndex === idx ? '#FFFFFF' : AppColors.TEXT_SECONDARY)
      .fontWeight(this.chartTypeIndex === idx ? FontWeight.Medium : FontWeight.Normal)
      .borderRadius(BorderRadius.FULL)
      .backgroundColor(this.chartTypeIndex === idx ? AppColors.PRIMARY : '#F0F0F5')
      .onClick(() => { this.chartTypeIndex = idx; })
  })
}

当用户点击"折线"时:

  • this.chartTypeIndex 变为 0
  • this.getCurrentType() 返回 DataPanelType.Line
  • DataPanel 重新渲染为折线图——7 个数据点用圆点标记,相邻点之间用直线连接
  • 折线的起伏变化让用户一眼就能看出"周六最高、周日最低"

当用户点击"环形"时:

  • this.chartTypeIndex 变为 1
  • this.getCurrentType() 返回 DataPanelType.Circle
  • DataPanel 重新渲染为环形图——7 个维度从中心辐射出去,端点连接形成七边形
  • 环形图让用户一眼就能看出各天之间的"均衡度"——越接近正七边形越均衡,越不规则说明每天运动量差异越大

折线图和环形图不是"哪个更好"的关系——它们展示数据的不同侧面。折线图擅长回答"趋势是什么"(哪天上去了?哪天下来了?),环形图擅长回答"均衡度如何"(每天运动量是否均匀?)。在真实的产品中,同时提供两种视图是一种"让用户选择自己偏好的信息摄入方式"的设计策略。

3.5 数据指标选择:三种运动数据的切换

数据指标选择使用了一个垂直排列的卡片列表:

ForEach(DATA_SETS, (ds: DataSet, idx: number) => {
  Row() {
    Text(ds.icon).fontSize(22)
    Column() {
      Text(ds.label)
        .fontColor(this.dataSetIndex === idx ? AppColors.PRIMARY : AppColors.TEXT_PRIMARY)
        .fontWeight(this.dataSetIndex === idx ? FontWeight.Medium : FontWeight.Normal)
      Text(`日均 ${ds.unit}`).fontSize(FontSize.CAPTION).fontColor(AppColors.TEXT_TERTIARY)
    }
    .alignItems(HorizontalAlign.Start).margin({ left: Spacing.SM })

    Blank()

    if (this.dataSetIndex === idx) {
      Row()
        .width(8).height(8).borderRadius(4)
        .backgroundColor(AppColors.PRIMARY)
    }
  }
  .width('100%')
  .backgroundColor(this.dataSetIndex === idx ? AppColors.PRIMARY_LIGHT : '#FFFFFF')
  .borderRadius(BorderRadius.MD)
  .border({ width: this.dataSetIndex === idx ? 1 : 0, color: AppColors.PRIMARY })
  .onClick(() => { this.dataSetIndex = idx; })
})

选中态的视觉反馈非常丰富——四项同时变化:

  1. 标签文字变色:选中项的文字变为主题色(AppColors.PRIMARY),未选中保持黑色
  2. 标签字重加粗:选中项变为 FontWeight.Medium
  3. 背景色变化:选中项背景变为 AppColors.PRIMARY_LIGHT(主题色的浅色版本)
  4. 边框出现:选中项出现 1vp 的主题色边框
  5. 圆点指示器:右侧出现一个 8x8 的实心圆点

这五项变化共同构成了强烈的"选中态"视觉信号。单一的变化(比如只改文字颜色)在阳光下或低亮度下可能不够明显,多个维度的同步变化确保了在任何光照条件下用户都能清晰辨识当前选中的是哪个数据集。
在这里插入图片描述

四、完整代码结构

页面组件树:

Column
├── Row(标题栏:"📈 运动数据")
└── Scroll
    └── Column
        ├── 数据面板区(深色背景 #1a1a2e)
        │   ├── Row(图标 + 数据集名称 + "最近 7 天")
        │   ├── DataPanel(values + max + type)
        │   └── Row(周标签:周一~周日 + 对应数值)
        ├── 图表类型切换区(白色背景)
        │   ├── Text("图表类型"标签)
        │   └── Row(折线/环形 两个 pill 按钮)
        └── 数据指标选择区(白色背景)
            ├── Text("数据指标"标签)
            └── Column
                ├── 🚶 每日步数(含选中圆点指示器)
                ├── 🔥 消耗卡路里
                └── ⏱️ 运动时长

4.1 关键代码解读

数据集切换的响应链

当用户点击"消耗卡路里"时:

  1. this.dataSetIndex = 1
  2. getCurrentDataSet() 返回 DATA_SETS[1](卡路里数据集)
  3. DataPanel 的 values 更新为 [320, 450, 280, 510, 380, 620, 210]
  4. DataPanel 的 valueColors 更新为红色系色组
  5. 标题行中的图标变为 🔥,标签变为"消耗卡路里"
  6. 周标签行中的 7 个数值同步更新
  7. 数据指标列表中"消耗卡路里"出现选中态(背景+边框+圆点)

单一的 @State 变量变化驱动了 7 个视觉区域的同步更新。这就是声明式 UI 的"深度响应"——你只需要改变数据,框架负责所有视图的同步。

Line 与 Circle 的切换

DataPanel 的 type 参数绑定到 getCurrentType()

DataPanel({
  values: this.getCurrentDataSet().values,
  max: 20000,
  type: this.getCurrentType()  // Line 或 Circle
})

getCurrentType() 返回 CHART_TYPES[this.chartTypeIndex].value。当用户切换图表类型按钮时,chartTypeIndex 变化,getCurrentType() 返回新值,DataPanel 切换渲染模式。这个切换是即时的,不需要任何动画或过渡代码。

4.2 max 值的考量

在我们的 Demo 中,max 被固定设置为 20000。这个值对于步数来说合理(最高 14800 约占 74%),但对于卡路里数据(最高 620)来说偏大——所有柱形都会缩在图表底部附近,图表的"利用率"较低。

在生产环境中,通常会为每种数据集动态计算合适的 max

getMaxForDataset(ds: DataSet): number {
  const dataMax = Math.max(...ds.values);
  return Math.ceil(dataMax * 1.3);  // 最大值上浮 30%
}

在我们的 Demo 中为了简洁,统一使用了固定 max: 20000。这在实际设计中是一个权衡:是让每个图表的 max 自适应(更好的数据展示,但切换数据集时图表比例会变化),还是使用固定 max(切换时图表比例不变,但某些数据集展示效果不佳)。两种方案各有优势,取决于你更看重"数据展示精度"还是"切换动画的流畅感"。

五、与 Progress 和 Gauge 的对比

在之前的文章中,我们分别探讨了 Progress(进度条组件)和 Gauge(仪表盘组件)。这三者虽然都是"数据可视化组件",但各自有最合适的场景:

特性 Progress Gauge DataPanel
数据维度 单一数值 单一数值 多个数值(数组)
视觉形态 线性条/环形环 弧形仪表盘 折线图/环形雷达图
最佳用途 展示完成度(下载 80%) 展示状态(CPU 45%) 展示对比(7 天步数)
核心语义 “完成了多少” “处于什么水平” “各维度如何对比”
色彩能力 单色/渐变 多段色带 每维度独立颜色

简单来说:

  • 如果你只需要展示一个数值(如"任务完成了 60%"),用 Progress
  • 如果你需要展示一个数值及其所处的"危险/安全"区间,用 Gauge
  • 如果你需要展示多个数值之间的对比和趋势,用 DataPanel

这三者不是互相替代的关系,而是同一体系中的不同工具——就像 Word 中的加粗、斜体和下划线一样,各有各的语义,在合适的场景下使用才能达到最佳的沟通效果。

六、总结

本文以运动数据看板为应用场景,深入解析了 ArkUI DataPanel 数据面板组件的核心 API、两种图表类型和数据集切换。

回顾本文覆盖的核心要点:

  1. DataPanel 的本质:多维度数据可视化组件,通过 values(数值数组)、max(最大值)、min(最小值)和 type(图表类型)定义数据域和视觉形态。与 Progress 和 Gauge 的单值展示不同,DataPanel 擅长"对比"——让多个数值在同一图表中对话。

  2. 两种图表类型DataPanelType.Line 折线图模式适合展示"趋势"——数据随时间如何起伏变化,一眼看到峰值和低谷。DataPanelType.Circle 环形图模式适合展示"均衡度"——各维度的值是否均匀,是否存在明显的"短板"或"长板"。两种模式互补而非对立,提供两种视图让用户自由切换是一种优秀的 UX 设计。

  3. 每个数据点的颜色valueColors(ResourceColor[]) 为每个维度指定独立颜色,颜色数组的长度应与 values 数组一致。通过为不同数据集使用不同的色系(蓝色步数、红色卡路里、绿色时长),颜色成为数据语义的一部分,而非单纯的装饰。

  4. 轨道背景色和线宽trackBackgroundColorstrokeWidth 控制图表的"底图层"和"数据层"的视觉权重。在深色背景上使用半透明白色轨道,在浅色背景上使用淡灰色轨道,保持图表在任何视觉环境中都有足够的可读性。

  5. 双重交互设计:我们的 Demo 提供了两层交互——图表类型切换(Line↔Circle)和数据集切换(步数↔卡路里↔时长)。两层切换是正交的(不互相影响),用户可以自由组合(如"看卡路里的环形图"或"看步数的折线图"),产生 2×3=6 种不同的视觉呈现。这种"正交交互"设计让简单的界面承载了丰富的探索可能性。

  6. 选中态的多维度反馈:数据指标列表的选中项通过文字颜色、字重、背景色、边框和圆点指示器五个维度同步表达"当前选中"。多维度反馈确保了在任何光照条件和视觉环境下,选中态都是清晰可辨的。

DataPanel 是 ArkUI 数据可视化组件家族中最"灵活"的成员。它不像 Progress 那样有明确的"从 0% 到 100%“的叙事,也不像 Gauge 那样有"绿区安全、红区危险"的紧急感——DataPanel 更像一块"画布”,让数据之间的对比关系自然浮现。当你需要在一个页面中展示多个相关数值的对比和趋势时,不要把它们堆成一排独立的 Progress 条——用一个 DataPanel,让数据"对话"起来。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐