鸿蒙数据可视化实践:柱状图与进度条的自定义组件构建

前言

在 HarmonyOS 6.0 应用开发中,数据可视化是提升用户体验的关键环节。无论是用电趋势分析还是账单占比展示,直观的图表远比纯数字列表更能帮助用户快速理解信息。本文将以“宿舍水电”管理应用中的“本周用电趋势”柱状图模块和“本月账单”进度条模块为例,深入解析如何在鸿蒙应用中不依赖第三方图表库,完全使用原生布局组件构建自定义数据可视化组件。
在这里插入图片描述

背景

在校园宿舍水电管理场景中,学生不仅需要知道当前余额和用量,更需要了解“用电趋势如何”“哪一天用电最多”“各项费用占比多少”等深度信息。传统的做法是直接展示数字表格,例如“周一 0.52 kWh,周二 0.46 kWh…”,用户需要在脑中自行对比数值大小。通过自定义柱状图,我们可以将每日用电量以条形高度直观呈现;通过带进度条的账单明细,用户可以一眼看出哪项费用占比最高。HarmonyOS 6.0 的布局组件足够灵活,完全可以通过 Row + Column + FractionallySizedBox 的组合实现柱状图,通过 LinearProgressIndicator 实现进度条。

HarmonyOS 6.0 跨端开发介绍

HarmonyOS 6.0 虽然提供了基础的图表组件,但很多场景下我们需要更灵活的自定义实现。柱状图的核心原理是通过 FractionallySizedBoxheightFactor 属性控制条形的高度占比——数值越大条形越高,数值越小条形越矮。这个因子可以是我们数据值除以最大值的比例,也可以直接使用原始数值(只要不超过 1)。而 LinearProgressIndicator 组件天然支持进度条样式,通过 value 属性控制填充比例,minHeight 控制粗细,backgroundColorvalueColor 分别控制背景色和填充色。这种纯声明式的图表构建方式完全避免了引入第三方库带来的包体积膨胀和性能开销。
在这里插入图片描述

开发核心代码

模块一:本周用电趋势柱状图的数据结构与布局策略

柱状图模块首先定义了一个 days 列表,每个元素包含星期缩写、用电量数值和主题色:

final days = [
  ('一', 0.52, _blue),
  ('二', 0.46, _cyan),
  ('三', 0.64, _amber),
  ('四', 0.58, _green),
  ('五', 0.82, _red),
  ('六', 0.74, _purple),
  ('日', 0.60, _blue),
];

这里的用电量数值(0.52、0.46、0.64…)实际上代表了条形高度占容器最大高度的比例。例如周五用电量最高为 0.82(即 82% 的相对高度),周二最低为 0.46(46% 的相对高度)。外层容器采用白色面板带浅蓝边框,圆角 24,标题区显示“本周用电趋势”主标题和“日均 11.8 kWh”的副标题。图表区域固定高度为 130 像素,外层 SizedBox 约束了整个图表的高度范围。

模块二:柱状图的逐日渲染与高度控制逻辑

图表区域的核心是一个 Row 组件,内部通过 days.map() 遍历生成 7 个 Expanded 子组件:

SizedBox(
  height: 130,
  child: Row(
    crossAxisAlignment: CrossAxisAlignment.end,
    children: days.map((day) {
      return Expanded(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 5),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              Expanded(
                child: Align(
                  alignment: Alignment.bottomCenter,
                  child: FractionallySizedBox(
                    heightFactor: day.$2,
                    child: Container(
                      width: 20,
                      decoration: BoxDecoration(
                        color: day.$3,
                        borderRadius: BorderRadius.circular(999),
                      ),
                    ),
                  ),
                ),
              ),
              const SizedBox(height: 9),
              Text(day.$1, ...),
            ],
          ),
        ),
      );
    }).toList(),
  ),
)

这段代码的布局层次比较深,需要仔细拆解。最外层 RowcrossAxisAlignment: CrossAxisAlignment.end 确保所有子组件底部对齐。每个 Expanded 占据等宽的横向空间,内部是一个 Padding 提供左右 5 像素的柱间间距。再内部是一个 ColumnmainAxisAlignment: MainAxisAlignment.end 将内容从底部开始排列。Column 内部包含两部分:上方的柱状条区域和下方的星期标签。柱状条区域由一个 Expanded 包裹的 Align 组件构成——Expanded 让这个区域占据除去星期标签高度之外的所有剩余空间,Alignalignment: Alignment.bottomCenter 确保条形从底部向上生长。核心的 FractionallySizedBox 通过 heightFactor 参数控制条形占父容器高度的比例,数值越大条形越高。条形本身是一个宽度 20 像素、带有主题色和全圆角的 Container。每个条形的颜色根据用电量不同而变化(周一蓝色、周二青色、周三琥珀色、周四绿色、周五红色、周六紫色、周日蓝色),这种颜色编码让高用电日和低用电日的视觉差异更加明显。
在这里插入图片描述

模块三:柱状图设计的关键技术点解析

这个自定义柱状图实现中有几个关键技术点值得注意。第一,FractionallySizedBoxheightFactor 取值范围是 0 到 1,如果原始数据超过 1(例如实际用电量是 2.5 kWh),需要先归一化处理——将所有数值除以最大值,得到相对比例。第二,使用 Expanded 包裹条形区域是为了让所有条形高度基于同一个基准线对齐,如果没有这个 Expanded,条形高度将无法统一标度。第三,Align 组件的 alignment: Alignment.bottomCenter 是实现“从底部向上生长”的关键,如果不设置这个对齐方式,条形会默认从顶部开始向下延伸。第四,每个条形使用不同的主题色可以增强数据的可区分性,在实际项目中也可以统一使用一种品牌色,在最高柱上使用高亮色。

模块四:本月账单进度条模块的整体结构

账单模块采用深色背景(_navy 深海军蓝),圆角 24,与页面的浅蓝背景形成对比,视觉上强调这是核心财务信息。标题区使用 _buildDarkTitle 方法(白色文字),显示“本月账单”和右侧的“5月”月份标识。下方连续排列了四个账单条目:公共照明分摊 ¥8.60、寝室基础电费 ¥72.40、生活用水 ¥19.20、空调用电 ¥36.80。每个条目通过 _buildBillLine 辅助方法构建,该方法接收标签、金额、进度值和主题色四个参数。

模块五:进度条条目的内部布局与进度指示器配置

_buildBillLine 方法的实现如下:

return Column(
  children: [
    Row(
      children: [
        Expanded(
          child: Text(label, style: TextStyle(color: Colors.white, fontWeight: FontWeight.w900)),
        ),
        Text(amount, style: TextStyle(color: color, fontWeight: FontWeight.w900)),
      ],
    ),
    const SizedBox(height: 8),
    ClipRRect(
      borderRadius: BorderRadius.circular(999),
      child: LinearProgressIndicator(
        value: value,
        minHeight: 8,
        backgroundColor: Colors.white.withValues(alpha: 0.12),
        valueColor: AlwaysStoppedAnimation<Color>(color),
      ),
    ),
  ],
);

每个账单条目是一个垂直列布局,上层是 Row 水平布局,左侧标签用 Expanded 撑开并左对齐(白色加粗),右侧金额用对应的主题色加粗显示。下层是 8 像素间距后的进度条。LinearProgressIndicatorvalue 参数控制填充比例(0.18 表示 18% 填充,0.62 表示 62% 填充),minHeight: 8 控制进度条粗细为 8 像素。backgroundColor 设置为白色叠加 0.12 透明度(极淡的白色半透明),valueColor 使用传入的主题色(青色、琥珀色、蓝色、绿色)。外层用 ClipRRect 包裹并设置圆角 999,确保进度条两端完全圆角。这里的进度值(0.18、0.62、0.32、0.48)代表该费用占总费用的比例——寝室基础电费占比最高(62%),公共照明分摊最低(18%)。用户看到进度条的长度就能直观感知各项费用的相对大小。

模块六:进度条设计的数据归一化处理

在实际项目中,传递给 LinearProgressIndicatorvalue 参数需要是 0 到 1 之间的比例值。如果原始数据是具体的金额,需要先计算总金额,再用单项金额除以总金额得到比例。例如本例中总费用为 ¥8.60 + ¥72.40 + ¥19.20 + ¥36.80 = ¥137.00,那么公共照明分摊的比例是 8.60 / 137 = 0.0628(代码中使用了 0.18 可能是基于估算)。正确的做法是在代码中动态计算比例:

final total = bills.fold(0.0, (sum, item) => sum + item.amount);
final ratios = bills.map((item) => item.amount / total);

这样可以确保进度条的比例始终正确,即使账单数据发生变化也能自动适配。
在这里插入图片描述

心得

通过实现柱状图和进度条这两个自定义图表组件,我深刻体会到 HarmonyOS 6.0 原生布局组件的灵活性和表达力。过去实现柱状图往往会引入第三方图表库如 MPAndroidChart 或 ECharts,虽然功能强大但会增加应用的包体积和初始化时间。而使用 FractionallySizedBox 配合 Row + Column 组合,只需不到 40 行代码就能实现一个完全可定制的柱状图,条形颜色、宽度、间距、圆角都可以精确控制。同样,LinearProgressIndicator 作为系统原生组件,性能和动画流畅度远超 WebView 套壳方案。另一个重要心得是关于数据归一化的必要性——无论是柱状图的 heightFactor 还是进度条的 value,都需要确保传入的比例值在 0 到 1 之间。如果数据波动较大(例如某天用电量是其他天的 5 倍),强行归一化会导致低值柱状条几乎不可见,这时可以考虑使用对数坐标或截断处理。此外,柱状图中每个条形使用不同颜色虽然美观,但可能让用户困惑颜色与数据的关系,更好的做法是在图表顶部添加图例说明。最后需要强调的是,这个柱状图实现目前仅支持单一系列数据,如果需要展示多系列对比(例如本周 vs 上周),则需要更复杂的布局设计。
在这里插入图片描述

总结

本文详细解析了“宿舍水电”管理应用中本周用电趋势柱状图和本月账单进度条两个自定义图表模块的完整实现。柱状图模块通过 Row + Expanded + FractionallySizedBox 的组合,实现了条形高度与用电量数据的绑定映射,7 个条形分别对应周一到周日,颜色根据用电量高低动态变化。进度条模块利用 LinearProgressIndicator 组件,为公共照明分摊、寝室基础电费、生活用水、空调用电四个账单条目分别展示了费用占比,进度条长度直观反映了各项费用的相对大小。两个模块共同展示了 HarmonyOS 6.0 声明式 UI 在数据可视化领域的强大能力——不依赖第三方库,仅用原生布局组件即可构建美观、直观、高性能的自定义图表。代码中的数据归一化处理、FractionallySizedBox 的高度控制、Align 的底部对齐策略、LinearProgressIndicator 的圆角裁剪等技巧,均可直接应用到其他鸿蒙项目中。后续技术博客将聚焦于表单交互和网络请求,包括宿舍切换的完整逻辑、缴费接口的调用以及图表数据的实时刷新,敬请期待。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐