当前位置: 首页 > news >正文

HarmonyOS应用开发实战:萌宠日记 - 用Stack绘制自定义图表

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 (i=0)0vp6.0
1 (i=1)36vp5.5
2 (i=2)72vp5.0
3 (i=3)108vp4.5
4 (i=4)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 * 50(5 个点均匀分布) // Y 位置:144 - (i * 18 + 36) // 图表区域高度 = 180vp // 数据映射范围:4.0-6.0(2.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 个原则:

  1. 数据映射:将数据值映射到像素坐标,确保比例准确
  2. 网格线辅助:水平网格线帮助用户读取数据值
  3. 标签清晰:坐标轴标签使用小字号,辅助阅读
  4. 颜色突出:数据点使用醒目的蓝色,网格线使用柔和的米色
  5. 容器统一:图表容器与其他卡片样式一致

10.2 萌宠日记体重图表总结

设计要素说明
数据点5 个圆形,蓝色
网格线5 条水平,米色
Y 轴范围4.0-6.05 个标签
X 轴标签5 个与数据点对齐
图表高度180vp适中
容器白色卡片12vp 圆角

7.1 属性对比

属性类型必填默认值说明
widthLength'100%'组件宽度
heightLength自适应组件高度
paddingPadding0内边距
marginMargin0外边距
backgroundColorColor透明背景颜色
borderRadiusLength0圆角半径
shadowShadow阴影效果

总结

本文从萌宠日记体重折线图实现出发,深入解析了使用 Stack 绘制自定义图表的完整方法:

  1. Stack 层叠:网格线背景 + 数据点前景叠加
  2. 网格线绘制:ForEach 渲染 5 条水平线
  3. 数据点绘制:Circle 组件 + position 定位
  4. 坐标轴标签:Y 轴 5 个标签,X 轴 5 个标签
  5. 图表标题:标题 + 单位说明
  6. 动态数据:用真实数据驱动图表渲染
  7. 折线连接:Path 组件绘制数据点之间的连线

使用基础组件绘制自定义图表,避免了引入第三方库的依赖,对于轻量级的数据可视化场景非常实用。

下一篇我们将深入Y 轴与 X 轴标签布局,解析图表坐标轴标签的精确对齐与布局设计。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

  • Stack 组件:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-stack
  • Circle 组件:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-circle
  • Path 组件:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-image
  • Row 组件: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
http://www.jsqmd.com/news/1242648/

相关文章:

  • Kimi网页解析能力深度拆解(工程师内部调试日志首次公开)
  • 从数据集到评估指标:FCGF在3DMatch/KITTI上的SOTA表现全揭秘
  • 为什么你的AI微服务总在凌晨3点丢事件?——基于237个真实故障日志的事件驱动架构AI化改造避坑清单
  • 3步搞定RPCS3模拟器:从零开始畅玩PS3游戏
  • C++ 各类特殊符号、运算符
  • EMIFA接口与NAND Flash驱动:状态监控、中断处理与电源管理详解
  • 关于 springmvc 中的 ResponseBody 和 RequestBody 两个注解的差别
  • 企业 AI 原生的数字员工架构设计和落地实践
  • McBSP时钟停止模式配置SPI通信:原理、配置与实战指南
  • TMS320F2837xD ADC中断与后处理模块(PPB)实战指南
  • 从Excel到BI:中小企业数据驱动决策工具的实操落地全流程
  • 为什么顶尖刑辩团队已停用传统数据库?秘塔AI法律检索实战对比:类案召回率高出传统方式3.8倍
  • 深入解析McBSP采样率生成器与异常处理机制
  • 深入解析eHRPWM寄存器:从时间基准到动作限定的电机控制核心
  • RFID 赋能汽车转向机装配工序智能化升级
  • 深耕海外制造展会,专业展台设计搭建公司精选,适配美国制造展落地需求 - 资讯报道
  • 嵌入式后台开发:守护进程制作与程序开机自启
  • Java程序员集体关注:飞算JavaAI炫技赛行业声量盘点,一场赛事如何撬动AI编程赛道
  • 如何5分钟搞定Mac双系统驱动:跨平台自动化工具终极指南
  • bootstrap-filestyle核心功能揭秘:拖拽上传、多文件选择与样式定制
  • Tack高级配置:深入理解Terraform模块化架构设计
  • AI编程如何重构事件驱动架构?揭秘头部科技公司正在悄悄使用的3层智能编排模型
  • 3大场景深度解析:Sandboxie启动故障的实战排查与解决方案
  • 当LLM开始为你的论文写“参考文献生成日志”:学术诚信新边界下的AI协同时代(IEEE伦理委员会2024预警报告核心结论)
  • 告别“对话框发图”!腾讯首个创意智能体工作室 Miora 国际版上线:多 Agent 协作,从 Brief 直达全套交付资产
  • 如何快速掌握gh_mirrors/bu/buildfirst项目:Grunt任务自动化入门教程
  • 如何快速完成语音模型微调:面向开发者的完整实战指南
  • 深入解析I2C总线时钟同步、仲裁机制与TMS320F2837xD中断编程
  • CST · FDTD · COMSOL 携手AI:超越单一工具的智能设计方法!
  • X-StereoLab与PyTorch深度集成:分布式训练的实现原理与代码示例