
HarmonyOS应用开发实战萌宠日记 - 用Stack绘制自定义图表前言数据可视化是健康记录页的核心功能通过图表直观展示宠物体重变化趋势。在萌宠日记中我们没有使用第三方图表库而是使用Stack 组件配合Circle 圆形和Row 网格线手动绘制了一个轻量级的折线图展示了 5 个数据点的体重变化。本文将从萌宠日记的体重折线图实现出发深入解析 Stack 层叠布局的原理、坐标系构建、数据点绘制以及如何用基础组件实现自定义图表。一、体重折线图整体设计1.1 图表结构┌─────────────────────────────────────┐ │ 体重变化 单位kg│ │ ┌─────────────────────────────────┐│ │ │ 6.0 ───────────────────────── ││ ← Y 轴标签 │ │ 5.5 ───────────────────────── ││ ← 网格线 │ │ 5.0 ───────────────────────── ││ │ │ 4.5 ───●───────●───────●──── ││ ← 数据点Circle │ │ 4.0 ───────────────────────── ││ │ └─────────────────────────────────┘│ │ 2.0 2.0 2.0 2.0 2.0 │ ← X 轴标签 └─────────────────────────────────────┘1.2 完整代码// HealthRecordPage.ets — 体重折线图 Column({ space: 12 }) { // 标题行 Row() { Text(体重变化) .fontSize(16) .fontWeight(FontWeight.Bold) .fontColor(#333333) Blank() Text(单位kg) .fontSize(12) .fontColor(#999999) } .width(100%) // 图表区域 Column() { Row() { // Y 轴标签 Column() { ForEach([6.0, 5.5, 5.0, 4.5, 4.0], (label: string) { Text(label) .fontSize(10) .fontColor(#999999) .height(36) .textAlign(TextAlign.End) .width(30) }) } // 图表区域 Column() { Stack() { // 网格线 ForEach([0, 1, 2, 3, 4], (i: number) { Row() .width(100%) .height(1) .backgroundColor(#F0EBE3) .position({ x: 0, y: i * 36 }) }) // 数据点 Row() { ForEach([0, 1, 2, 3, 4], (i: number) { Circle() .width(8) .height(8) .fill(#42A5F5) .position({ x: i * 50, y: 144 - (i * 18 36) }) }) } .width(100%) .height(180) } .width(100%) .height(180) } .layoutWeight(1) } .width(100%) // X 轴标签 Row() { Text().width(30) // 与 Y 轴标签对齐 ForEach([2.0, 2.0, 2.0, 2.0, 2.0], (label: string) { Text(label) .fontSize(10) .fontColor(#999999) .layoutWeight(1) .textAlign(TextAlign.Center) }) } .width(100%) } .padding(12) .backgroundColor(#FFFFFF) .borderRadius(12) } .width(100%) .padding({ left: 16, right: 16 })提示体重折线图完全使用Stack 层叠布局实现。Stack 将网格线背景层和数据点前景层叠加在一起通过position属性精确控制每个元素的位置。这是一种轻量级的自定义图表方案无需引入第三方图表库。二、Stack 层叠布局2.1 Stack 组件Stack() { // 背景层网格线 // 前景层数据点 } .width(100%) .height(180)2.2 层叠关系Stack 层叠从下到上 ↓ 第一层网格线5 条水平线 ↓ 第二层数据点5 个蓝色圆点 ↓ 网格线作为背景数据点作为前景叠加显示三、网格线绘制3.1 水平网格线// 5 条水平网格线每条间隔 36vp ForEach([0, 1, 2, 3, 4], (i: number) { Row() .width(100%) // 撑满宽度 .height(1) // 线高 1vp .backgroundColor(#F0EBE3) // 米色网格线 .position({ x: 0, y: i * 36 }) // 垂直位置0, 36, 72, 108, 144 })3.2 网格线间距网格线索引Y 位置对应 Y 轴标签0 (i0)0vp6.01 (i1)36vp5.52 (i2)72vp5.03 (i3)108vp4.54 (i4)144vp4.0四、数据点绘制4.1 Circle 组件// 数据点 — 蓝色圆形 Circle() .width(8) // 圆点直径 8vp .height(8) // 圆点直径 8vp .fill(#42A5F5) // 蓝色填充 .position({ x: i * 50, y: 144 - (i * 18 36) }) // 位置计算4.2 数据点位置计算// 数据点位置计算 // X 位置i * 505 个点均匀分布 // Y 位置144 - (i * 18 36) // 图表区域高度 180vp // 数据映射范围4.0-6.02.0 范围 // 每单位高度 180 / 2.0 90vp // 数据点分布 // 点 0: 4.0kg → y 144 // 点 1: 4.3kg → y 126 // 点 2: 4.5kg → y 108 // 点 3: 4.7kg → y 90 // 点 4: 5.0kg → y 72五、坐标轴标签5.1 Y 轴标签// Y 轴标签左侧 Column() { ForEach([6.0, 5.5, 5.0, 4.5, 4.0], (label: string) { Text(label) .fontSize(10) .fontColor(#999999) .height(36) // 与网格线间距一致 .textAlign(TextAlign.End) // 右对齐 .width(30) // 固定宽度 }) }5.2 X 轴标签// X 轴标签底部 Row() { Text().width(30) // 与 Y 轴标签对齐 ForEach([2.0, 2.0, 2.0, 2.0, 2.0], (label: string) { Text(label) .fontSize(10) .fontColor(#999999) .layoutWeight(1) // 与数据点对齐 .textAlign(TextAlign.Center) }) }六、图表标题行6.1 标题设计Row() { Text(体重变化) .fontSize(16) .fontWeight(FontWeight.Bold) Blank() Text(单位kg) .fontSize(12) .fontColor(#999999) } .width(100%)七、图表容器样式7.1 容器Column() { // 图表内容 } .padding(12) .backgroundColor(#FFFFFF) .borderRadius(12)八、动态数据驱动8.1 数据绑定// 使用真实数据驱动图表 State weightData: number[] [4.0, 4.3, 4.5, 4.7, 5.0] build() { // 根据 weightData 动态绘制数据点 ForEach(this.weightData, (weight: number, index: number) { Circle() .width(8).height(8) .fill(#42A5F5) .position({ x: index * 50, y: 180 - ((weight - 4.0) / 2.0 * 180) - 18 }) }) }九、图表扩展9.1 折线连接// 在数据点之间添加连接线 // 使用 Path 组件绘制折线 Path() .commands(M 0 144 L 50 126 L 100 108 L 150 90 L 200 72) .stroke(#42A5F5) .strokeWidth(2) .fill(none)十、最佳实践10.1 自定义图表设计原则有序列表 — 自定义图表设计的 5 个原则数据映射将数据值映射到像素坐标确保比例准确网格线辅助水平网格线帮助用户读取数据值标签清晰坐标轴标签使用小字号辅助阅读颜色突出数据点使用醒目的蓝色网格线使用柔和的米色容器统一图表容器与其他卡片样式一致10.2 萌宠日记体重图表总结设计要素值说明数据点5 个圆形蓝色网格线5 条水平米色Y 轴范围4.0-6.05 个标签X 轴标签5 个与数据点对齐图表高度180vp适中容器白色卡片12vp 圆角7.1 属性对比属性类型必填默认值说明widthLength否100%组件宽度heightLength否自适应组件高度paddingPadding否0内边距marginMargin否0外边距backgroundColorColor否透明背景颜色borderRadiusLength否0圆角半径shadowShadow否无阴影效果总结本文从萌宠日记的体重折线图实现出发深入解析了使用 Stack 绘制自定义图表的完整方法Stack 层叠网格线背景 数据点前景叠加网格线绘制ForEach 渲染 5 条水平线数据点绘制Circle 组件 position 定位坐标轴标签Y 轴 5 个标签X 轴 5 个标签图表标题标题 单位说明动态数据用真实数据驱动图表渲染折线连接Path 组件绘制数据点之间的连线使用基础组件绘制自定义图表避免了引入第三方库的依赖对于轻量级的数据可视化场景非常实用。下一篇我们将深入Y 轴与 X 轴标签布局解析图表坐标轴标签的精确对齐与布局设计。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源Stack 组件https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-stackCircle 组件https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-circlePath 组件https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-imageRow 组件https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-row数据可视化https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/data-visualization颜色使用规范https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/color-usage布局基础https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkui-layout动画开发https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-animation