鸿蒙 ArkTS 实战:Weight Trend Book 从健康数据到交互面板完整解析
鸿蒙 ArkTS 实战:Weight Trend Book 从健康数据到交互面板完整解析
前言
体重趋势记录本 是一个面向 健康记录与运动管理 的鸿蒙 ArkTS 小应用。记录体重、身高、目标体重、BMI 和趋势变化,适合长期体重管理。 本文基于 entry/src/main/ets/pages/Index.ets 的真实源码结构展开,分析它的 数据模型、@State 状态、业务方法、Builder 组件 和页面布局,形成一篇可直接发布的 CSDN 技术文章。

图示说明:页面以健康数据记录、目标追踪、趋势反馈和操作按钮为核心,适合做健康类 ArkTS 单页应用练习。
一、应用定位与业务闭环
1.1 场景定位
Weight Trend Book 面向日常健康管理。用户可以记录数据、查看趋势、推进计划或完成打卡,页面会根据状态变化即时刷新结果。
| 业务环节 | 页面表现 | 技术落点 |
|---|---|---|
| 数据记录 | 输入框、按钮、列表 | TextInput、Button、ForEach |
| 目标追踪 | 数值、百分比、连续天数 | private 计算方法 |
| 状态反馈 | 提示文案和颜色 | @State 驱动 |
| 趋势展示 | 条目卡片或趋势行 | Builder 组件 |
| 操作闭环 | 添加、完成、导出、打卡 | 业务方法 |
1.2 阅读路径
- 先确认接口定义,理解业务数据结构。
- 再分析状态字段,判断页面刷新来源。
- 接着拆解方法,明确新增、统计、完成和反馈动作。
- 最后看 Builder,理解重复 UI 如何组织。
核心原则:健康类小工具的数据必须可追踪,状态必须少而明确,反馈必须在用户操作后立即出现。
二、工程结构与入口页面
2.1 目录结构
核心代码位于 entry/src/main/ets/pages/Index.ets。
Weight Trend Book
├── AppScope
├── entry
│ └── src
│ └── main
│ ├── ets
│ │ └── pages
│ │ └── Index.ets
│ └── module.json5
├── hvigor
├── build-profile.json5
└── oh-package.json5
2.2 页面入口
@Entry
@Component
struct Index {
build() {
Scroll() {
Column() {
// 健康数据面板
}
}
}
}
2.3 技术组成
| 技术点 | 作用 | 本项目价值 |
|---|---|---|
| ArkTS | 类型和逻辑 | 组织健康数据 |
| ArkUI | 声明式 UI | 构建页面结构 |
| @State | 响应式状态 | 触发界面刷新 |
| @Builder | UI 片段复用 | 统一卡片和趋势样式 |
| Hvigor | 构建工具 | 生成鸿蒙应用包 |
三、数据模型设计
3.1 接口清单
源码中的接口包括:$iface。
interface WeightEntry {
id: number;
title?: string;
value?: number;
done?: boolean;
note?: string;
}
如果项目没有单独定义接口,它会直接通过 @State 保存核心字段,例如阶段、秒数、声音、分钟数和连续天数。
3.2 字段分类
| 字段类型 | 常见字段 | 作用 |
|---|---|---|
| 标识字段 | id | 列表渲染 key |
| 记录字段 | steps、weight、score、pressure | 健康指标 |
| 状态字段 | done、phase、message | 操作反馈 |
| 目标字段 | goal、 argetDays、minutes | 目标追踪 |
| 分类字段 | ype、category、sound | 筛选或模式切换 |
3.3 模型价值
健康类应用不是只展示一个数字,而是要让数字和日期、目标、趋势、动作关联起来。接口定义让每条记录更稳定,也便于后续接入本地存储。
四、@State 状态拆解
4.1 状态清单
项目中的状态包括:$state。
@State records: WeightEntry[] = [];
@State draftValue: string = '';
@State goal: number = 0;
@State message: string = '';
4.2 状态分类表
| 状态类别 | 示例 | 说明 |
|---|---|---|
| 列表状态 | $primaryState | 保存记录或动作列表 |
| 输入状态 | weightDraft、sysDraft、draft | 接收用户输入 |
| 目标状态 | goal、 argetDays、minutes | 计算完成度 |
| 运行状态 | ||
| emaining、seconds、phase | 控制计时或训练节奏 | |
| 反馈状态 | message、 | |
| eport | 展示操作结果 |
4.3 状态更新策略
列表类状态使用新数组替换,计时类状态直接更新当前数值。
private addRecord(): void {
const item: WeightEntry = {
id: Date.now(),
title: '新记录',
done: false
};
this.records = [item, ...this.records];
}
五、核心方法解析
5.1 方法清单
源码中的核心方法包括:$method。
| 方法类型 | 作用 | 典型场景 |
|---|---|---|
| 统计方法 | 计算百分比、平均值、异常数 | 步数、体重、血压 |
| 新增方法 | 写入新记录 | 饮食、体重、跑步 |
| 计时方法 | 推进剩余秒数 | 拉伸、冥想 |
| 打卡方法 | 记录完成状态 | 戒糖、训练 |
| 趋势方法 | 渲染历史走势 | 体重、步数、训练 |
5.2 新增记录
private addEntry(): void {
const value = this.draftValue.trim();
if (value.length === 0) {
return;
}
this.records = [{ id: Date.now(), title: value, done: false }, ...this.records];
this.draftValue = '';
}
5.3 统计计算
private progress(): number {
if (this.goal === 0) {
return 0;
}
return Math.min(100, Math.round(this.currentValue / this.goal * 100));
}
5.4 完成动作
private finish(id: number): void {
this.records = this.records.map((item: WeightEntry) => {
return item.id === id ? { ...item, done: true } : item;
});
}
六、Builder 组件复用
6.1 Builder 清单
源码中的 Builder 组件包括:$builder。
@Builder
private card(item: WeightEntry) {
Column() {
Text(item.title ?? '记录')
.fontSize(16)
.fontWeight(FontWeight.Bold)
}
.padding(14)
.backgroundColor('#FFFFFF')
.borderRadius(8)
}
6.2 复用价值
| Builder | 适用内容 | 价值 |
|---|---|---|
| card | 历史记录、动作条目 | 统一列表样式 |
| rend | 趋势数据 | 统一可视化结构 |
| abs | 分类切换 | 统一选中态 |
| soundTabs | 声音模式 | 统一模式按钮 |
6.3 分工边界
Builder 不处理复杂计算,只接收状态并渲染 UI。计算逻辑放在 private 方法中,能降低页面耦合。
七、页面布局结构
7.1 常见结构
Scroll() {
Column() {
// 顶部目标区
// 输入区
// 趋势区
// 记录列表
}
.padding(16)
}
.backgroundColor('#F6F8FA')
7.2 页面分区
| 区域 | 内容 | 作用 |
|---|---|---|
| 顶部目标区 | 当前值、目标、百分比 | 形成即时反馈 |
| 输入区 | 数值或文本输入 | 写入记录 |
| 控制区 | 计时、模式、声音 | 控制训练流程 |
| 趋势区 | 周数据或历史记录 | 观察变化 |
| 记录区 | 卡片列表 | 复盘历史 |
7.3 列表渲染
ForEach(this.records, (item: WeightEntry) => {
this.card(item)
}, (item: WeightEntry) => item.id.toString())
八、交互链路
8.1 输入链路
TextInput({ text: this.draftValue })
.onChange((value: string) => {
this.draftValue = value;
})
8.2 操作链路
Button('记录')
.onClick(() => {
this.addEntry();
})
8.3 反馈链路
private setMessage(text: string): void {
this.message = text;
}
实现重点:输入只改草稿状态,按钮只调用方法,方法只更新状态,UI 只读取状态。
九、健康数据的可视化表达
9.1 指标类型
| 指标 | 表达方式 | 适用项目 |
|---|---|---|
| 百分比 | 进度条、数字 | 步行目标、戒糖目标 |
| 趋势 | 历史列表、折线模拟 | 体重、步数 |
| 计时 | 秒数、阶段 | 拉伸、冥想 |
| 异常 | 红色标签 | 血压记录 |
| 热量 | 数值统计 | 饮食照片日记 |
9.2 颜色语义
绿色表示完成或正常,橙色表示待关注,红色表示异常,蓝色表示当前模式或主操作。
9.3 文案策略
健康类工具要避免模糊文案。按钮应直接写“记录”“打卡”“完成”“导出”,状态应明确写“正常”“异常”“已完成”“进行中”。
十、运行与调试
10.1 DevEco Studio 运行
# 打开项目目录
# 同步依赖
# 选择 entry 模块
# 运行到模拟器或真机
10.2 Hvigor 构建
hvigorw clean
hvigorw assembleHap
10.3 调试关注点
| 调试项 | 判断标准 |
|---|---|
| 输入更新 | onChange 后状态变化 |
| 目标计算 | 百分比不超过 100 |
| 计时逻辑 | 秒数或阶段可推进 |
| 列表刷新 | 新增后记录出现在页面 |
| 异常判断 | 标签颜色和统计一致 |
十一、可维护性分析
11.1 文件规模
当前 Index.ets 约 68 行,适合作为单页健康工具示例。后续复杂度提高时,可以拆分为目标卡、输入表单、趋势图和记录列表组件。
11.2 状态来源
能从记录列表计算出的值,不需要额外保存为状态。这样能避免统计值和源数据不一致。
private recordCount(): number {
return this.records.length;
}
11.3 输入校验
健康数据通常需要基础校验,例如数字必须大于 0,血压值需要区间判断,体重和热量不能为空。
十二、扩展方向
12.1 本地存储
interface HealthRecordStore {
id: number;
type: string;
payload: string;
createdAt: number;
}
12.2 图表增强
可以加入折线图、柱状图或日历热力图,增强趋势可读性。
12.3 提醒能力
计时器、戒糖、喝水、拉伸等场景都可以继续接入通知提醒。
十三、同类模板抽象
13.1 数据模板
interface HealthItem {
id: number;
value: number;
note: string;
done: boolean;
}
13.2 状态模板
@State items: HealthItem[] = [];
@State draftValue: string = '';
@State message: string = '';
13.3 组件模板
@Builder
private itemCard(item: HealthItem) {
Row() {
Text(${item.value}).layoutWeight(1)
Text(item.done ? '已完成' : '进行中')
}
.padding(12)
.backgroundColor('#FFFFFF')
}
十四、实现亮点
14.1 业务模型清楚
Weight Trend Book 使用 $iface 组织健康数据,能把记录、目标和反馈连接起来。
14.2 状态链路明确
$state 覆盖了页面关键状态,便于调试输入、目标、记录和反馈。
14.3 方法职责集中
$method 将统计、记录、计时和打卡逻辑集中管理,避免 UI 内直接堆业务规则。
十五、总结
Weight Trend Book 是一个典型的鸿蒙 ArkTS 健康类单页应用。它通过 $iface 建模健康数据,通过 $state 保存目标、输入、运行和反馈状态,通过 $method 完成记录、统计、计时或打卡,再通过 $builder 渲染趋势和卡片。
这类应用的核心是“数据可追踪、状态可解释、反馈可感知”。掌握这套结构后,可以继续扩展更多健康管理场景,例如饮水、运动、睡眠、体重、血压、冥想和习惯打卡。
如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!
相关资源:
更多推荐




所有评论(0)