鸿蒙原生应用 ArkUI 实战:手写柱状图 + 双层进度条 —— 订阅统计页的纯 ArkUI 数据可视化架构
鸿蒙原生应用 ArkUI 实战:手写柱状图 + 双层进度条 —— 订阅统计页的纯 ArkUI 数据可视化架构
App 54「订阅服务管理」统计页(ProfileTab),主题色
#9B51E0梦幻紫,4 个 Tab 分别为首页(📺)、订阅(📋)、日历(📅)、统计(📊)。本篇基于54-sub-mgr/entry/src/main/ets/pages/ProfileTab.ets(共 108 行)逐段拆解,附 4 张实机截图。

一、整体结构:三区"年度卡 + 柱状图 + 占比图"布局
统计页是"年度总览 + 月度趋势 + 分类结构"的分析布局——3 个 @Builder 块:
build() {
Column() {
this.Header()
Scroll() {
Column({ space: 14 }) {
this.YearCard()
this.BarChart()
this.CatChart()
}
.width('100%')
.padding({ left: D.pad, right: D.pad, top: 14, bottom: D.pad + this.safeBottom + 20 })
}
.layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top)
}
.width('100%').height('100%').backgroundColor(C.bg)
}
3 块结构:YearCard(紫色渐变年度卡)→ BarChart(月度柱状图)→ CatChart(分类占比进度条)。统计页是"分析页"——先看年度总结(640 元、日均 1.75、同比 -8%),再看月度趋势(6 月最高 61),最后看分类结构(视频占 47%)。总览 → 趋势 → 结构三层递进,是系列中少见的"纯数据可视化页"(无用户卡/菜单)。
二、数据模型 + 动态最大值
interface CatStat {
id: number;
name: string;
amount: number;
percent: number;
color: string;
}
private months: string[] = ['3月', '4月', '5月', '6月', '7月', '8月'];
private monthVals: number[] = [46, 53, 53, 61, 53, 53];
private cats: CatStat[] = [
{ id: 1, name: '视频娱乐', amount: 300, percent: 47, color: '#E63946' },
{ id: 2, name: '软件工具', amount: 200, percent: 31, color: '#9B51E0' },
{ id: 3, name: '云服务', amount: 140, percent: 22, color: '#4A90D9' }
];
months 6 个月 + monthVals 对应支出(6 月最高 61);cats 3 分类(视频 47%/软件 31%/云服务 22%),各带品牌色。47+31+22 = 100%,300+200+140 = 640——分类结构完全对账。
private maxVal(): number {
let m = 1;
this.monthVals.forEach((v: number) => { if (v > m) { m = v; } });
return m;
}
maxVal() 动态求最大值(61)——柱高相对最大值缩放,默认 m = 1 避免除零。即使后续月份数据变化,图表自动适配最高柱,无需改代码。
三、Header + YearCard:年度渐变卡
Header() {
Row() { Text('支出统计').fontSize(20).fontWeight(FontWeight.Bold).fontColor(C.text) }
.width('100%').height(this.safeTop + 56).padding({ top: this.safeTop, left: D.pad, right: D.pad })
.backgroundColor(C.card).alignItems(VerticalAlign.Bottom)
}
YearCard() {
Column({ space: 8 }) {
Text('¥640').fontSize(36).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
Text('本年度订阅总支出').fontSize(13).fontColor('#FFFFFF').opacity(0.9)
Text('日均 ¥1.75 · 相比去年 -8%').fontSize(12).fontColor('#FFFFFF').opacity(0.85)
}
.width('100%').padding(24).borderRadius(D.rLg)
.linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] })
}
逐点拆解:
- 主金额 36 号:
¥640用 36 号 Bold 白字——比首页的 32 号更大(统计页是全屏分析,年度总额是唯一焦点)。 - 副文案两级透明度:"本年度订阅总支出"13 号 90% + "日均 ¥1.75 · 相比去年 -8%"12 号 85%——两级递减(0.9 → 0.85),日均(640÷365≈1.75)与同比是"平均 + 对比"两个分析维度。
- 数据自洽:640 = 首页"年度支出 ¥640" = 分类合计 300+200+140 = 640——三个数据源完全一致。

四、BarChart:手写月度柱状图
BarChart() {
Column({ space: 14 }) {
Text('月度支出趋势').fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.text).width('100%')
Row() {
ForEach(this.months, (m: string, idx: number) => {
Column({ space: 6 }) {
Text('¥' + this.monthVals[idx]).fontSize(9).fontColor(C.textDim)
Column()
.width(24).height(this.monthVals[idx] * 110 / this.maxVal())
.backgroundColor(C.primary).borderRadius({ topLeft: 4, topRight: 4 })
Text(m).fontSize(10).fontColor(C.textSub)
}.layoutWeight(1).justifyContent(FlexAlign.End)
}, (m: string) => m)
}.width('100%').height(160).alignItems(VerticalAlign.Bottom)
}
.width('100%').padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke })
}
逐点拆解:
- 纯 ArkUI 柱状图:
Column().width(24).height(val * 110 / maxVal())——柱高 = 数值 × 110 ÷ 最大值(6 月 61 元最高占满 110,其余按比例),不依赖第三方图表库,用基础组件手绘。 - 柱体细节:24 宽紫色柱 +
borderRadius({ topLeft: 4, topRight: 4 })——只圆顶部两角(柱底贴基线不需要圆角)。 - 柱顶金额 + 底部月份:9 号淡灰金额(¥46)+ 10 号月份(3月)——每根柱上下两个标签,金额在柱顶、月份在柱底。
- 底部对齐:
height(160).alignItems(VerticalAlign.Bottom)+ 每列justifyContent(FlexAlign.End)——所有柱底部对齐,形成标准柱状图形态。 - 趋势解读:3 月最低 46、6 月最高 61、其余稳定 53——8 月 53 ≈ 首页月付 53.30(四舍五入),趋势图与当月数据对账。

五、CatChart:双层 Stack 进度条
CatChart() {
Column({ space: 14 }) {
Text('分类占比').fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.text).width('100%')
ForEach(this.cats, (c: CatStat) => {
Column({ space: 6 }) {
Row() {
Text(c.name).fontSize(13).fontColor(C.text).layoutWeight(1)
Text('¥' + c.amount + ' · ' + c.percent + '%').fontSize(12).fontColor(C.textDim)
}.width('100%')
Stack({ alignContent: Alignment.Start }) {
Row().width('100%').height(8).backgroundColor(C.cardSoft).borderRadius(4)
Row().width(c.percent + '%').height(8).backgroundColor(c.color).borderRadius(4)
}.width('100%')
}.width('100%')
}, (c: CatStat) => c.id.toString())
}
.width('100%').padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke })
}
逐点拆解:
- 双层 Stack 进度条:
Stack({ alignContent: Alignment.Start })叠放灰底条(100%)+ 彩色条(percent%)——底条width('100%')淡灰圆角 8 高,彩色条width(c.percent + '%')按占比拉长(47% 红条最长)。 - 彩色分类条:视频红/软件紫/云服务蓝——颜色编码分类(与首页订阅品牌色同体系),视频娱乐 47% 红条最长(占近一半)。
- 名称 + 金额百分比:13 号分类名 + 12 号淡灰"¥300 · 47%"——金额与占比同行,信息紧凑。
- 百分比字符串:
width(c.percent + '%')是字符串拼接——ArkUI 宽度支持百分比字符串('47%'),若直接写width(c.percent)数字会被当 vp 值(47vp)而非 47%。

六、跨页数据自洽:统计分析的全局闭环
Text('¥640').fontSize(36).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
Text('¥640').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
统计页"¥640 本年度"与首页"年度支出 ¥640"完全一致——首页给结论、统计页给分解(月度趋势 + 分类结构)。统计页"8月 ¥53"≈ 首页"本月 53.30"(四舍五入),柱状图最后一个柱就是首页当月支出。分类 300+200+140 = 640 = 年度总额——分类结构完全对账。
七、系列横向对比:数据可视化页面的两种形态
| 统计页 | 代表 App | 图表类型 | 核心特色 | 主题色 |
|---|---|---|---|---|
| 趋势 + 结构双图 | App 54 订阅 | 柱状图 + 进度条 | 三图组合(总览/趋势/结构) | #9B51E0 梦幻紫 |
| 手写柱状图 | App 47 班费 | 柱状图 + 进度条 | 收支分析 | #00A862 翡翠绿 |
| 环形进度 | App 39 兴趣 | 环形 95% | 单环比例 | #FF6B9D 粉 |
差异点:App 54 的"趋势 + 结构双图组合"——柱状图看"月度趋势"(时间维度:6 月最高)、进度条看"分类结构"(空间维度:视频占 47%),时间与结构两个分析维度一页给全,是系列中分析维度最完整的统计页。同时它是系列中极少数没有用户卡/菜单的纯数据页(其他 App 的第 4 Tab 多为个人中心)。
八、开发者视角:调试与验证技巧
- 柱高比例公式:
height(val * 110 / maxVal())——固定系数 110 是"最高柱的像素高度",若图表高度 160 而最高柱 110,剩余 50 留给金额标签和月份标签;用"系数 = 图表高 - 标签预留"的公式推导。 - 百分比字符串:
width(c.percent + '%')拼接——ArkUI 宽度支持百分比字符串,但 percent 是数字(47),拼接 '47%' 才生效;直接写width(47)会被当 47vp。 - 进度条圆角:双层条都是
borderRadius(4)——高度 8 配圆角 4 是标准比例(4 = 8÷2 形成半圆头),圆角大于高度一半会出现怪异形状。 - 空数据防护:
maxVal()默认m = 1(避免除零)——数据全为 0 时柱高 0、maxVal 返回 1,防止除以零崩溃;产品化应展示空状态。
九、演进方向
| 方向 | 当前状态 | 产品化演进 |
|---|---|---|
| 月度动态 | 硬编码 6 个月 | 从扣款记录动态聚合月度支出 |
| 分类动态 | 硬编码 3 分类 | 从订阅分类自动计算占比 |
| 预算对比 | 仅同比 -8% | 设定年度预算 + 进度条 + 预测 |
| 环比箭头 | 无 | 柱状图叠加环比变化箭头 |
| 导出报表 | 无 | 导出图片/表格 + 年度订阅报告 |
十、一句话总结
统计页以"手写柱状图 + 双层 Stack 进度条"完成了订阅支出的全面分析。height(val * 110 / maxVal()) 纯 ArkUI 柱状图(不引图表库 + 底部对齐 + 顶部圆角)、Stack 双层进度条(灰底 100% + 彩色 percent% + width(c.percent + '%') 百分比字符串)、36 号年度总额 + 两级透明度副文案——108 行代码,一个"年度总览 → 月度趋势 → 分类结构"的纯数据可视化页,也是"手绘图表"型统计页的标准范式。

更多推荐




所有评论(0)