# 鸿蒙 HarmonyOS 应用开发实战(第34期)|计步器(Pedometer)— Progress Ring 环形进度与运动数据看板
一、应用概述
计步器(Pedometer) 是一款模拟步数追踪的健康应用。它通过随机算法模拟走路步数增长,利用环形进度条(Progress Ring)直观展示日行万步的完成进度,并同步计算行走距离(公里)和消耗的卡路里。用户可以通过「开始走路」和「暂停」按钮控制模拟过程,「重置」按钮清空数据重新开始。
本应用是 HarmonyOS Progress 组件 Ring(环形) 类型的完美应用场景。相比于传统的水平进度条,环形进度条具有更强的视觉冲击力和空间利用率,广泛用于运动健康、设备状态、任务完成度等场景。此外,应用还展示了 setInterval 高频更新(每 500ms)与数据衍生的高级用法。
二、技术架构与组件树
2.1 整体架构
@Entry @Component struct index34
├── Column (根容器)
│ ├── Row (顶部导航栏)
│ └── Column (主内容区, 居中)
│ ├── Stack (环形进度 + 中心数据)
│ │ ├── Progress (Ring 类型, 200×200)
│ │ └── Column (中心内容)
│ │ ├── Text ("🚶", 40px)
│ │ ├── Text (steps 数字, 36px)
│ │ └── Text ("步")
│ ├── Text (状态提示: "还剩 X 步" / "🎉 达成目标!")
│ ├── Row (统计面板)
│ │ ├── Column (公里数)
│ │ └── Column (卡路里)
│ ├── Row (按钮组)
│ │ ├── Button ("▶️ 开始走路" / "⏸️ 暂停")
│ │ └── Button ("🔄 重置")
│ └── Row (目标显示)
2.2 状态变量与常量
@State steps: number = 0; // 当前步数
@State target: number = 10000; // 目标步数(默认一万)
@State isWalking: boolean = false; // 是否行走中
intervalId: number = -1; // 定时器 ID
核心变量仅三个 @State,但通过 getter 衍生出了 progress、kilometers、calories、status 四个计算属性,体现了「最小化状态、最大化衍生」的 ArkTS 最佳实践。
三、核心代码深度解析
3.1 模拟行走的定时器逻辑
startWalking(): void {
if (this.isWalking) return;
this.isWalking = true;
this.intervalId = setInterval((): void => {
this.steps += Math.floor(Math.random() * 5) + 1;
if (this.steps >= 99999) {
this.stopWalking();
}
}, 500);
}
stopWalking(): void {
clearInterval(this.intervalId);
this.isWalking = false;
}
模拟算法:每 500ms,步数增加 Math.floor(Math.random() * 5) + 1,即每次增加 1~5 步(均匀分布)。这种算法模拟了真实走路时步频的微小波动——每秒大约增加 2~10 步,相当于每分钟 120~600 步,接近人类正常步行速度(约每分钟 100~120 步)。
安全上限:当 steps >= 99999 时自动停止,防止步数溢出或 UI 显示异常。99999 是五位数上限,超过了常见的日行步数范围(一般不超过 5 万步)。
3.2 派生数据的计算属性
get progress(): number {
return Math.min((this.steps / this.target) * 100, 100);
}
get kilometers(): string {
return (this.steps * 0.75 / 1000).toFixed(2);
}
get calories(): number {
return Math.floor(this.steps * 0.04);
}
get status(): string {
if (this.steps >= this.target) return '🎉 达成目标!';
return '还剩 ' + (this.target - this.steps) + ' 步';
}
| 属性 | 公式 | 说明 |
|---|---|---|
progress |
steps/target × 100,上限 100% |
环形进度条百分比 |
kilometers |
steps × 0.75m / 1000 |
假设步幅 0.75 米,保留两位小数 |
calories |
steps × 0.04 |
假设每步消耗 0.04 千卡 |
status |
条件判断 | 达目标时庆祝,否则显示剩余步数 |
参数说明:
- 步幅 0.75 米是亚洲成年人的平均步幅
- 每步 0.04 千卡是基于体重 60kg 的估算值
这些参数可以在未来版本中改为用户可配置项。
3.3 Progress Ring 环形进度条
Stack() {
Progress({ value: this.progress, total: 100, type: ProgressType.Ring })
.width(200).height(200)
.style({ strokeWidth: 18 })
.color('#4CAF50')
Column() {
Text('🚶').fontSize(40)
Text(this.steps.toLocaleString()).fontSize(36).fontWeight(FontWeight.Bold)
Text('步').fontSize(16).fontColor('#888')
}
}
Progress 组件详解:
Progress 是 HarmonyOS 内置的进度条组件,支持四种类型:
ProgressType.Linear:水平线性进度条ProgressType.Ring:环形进度条ProgressType.Eclipse:椭圆形进度条ProgressType.Scale:刻度盘进度条
本例使用 Ring 类型,主要参数:
| 参数 | 值 | 说明 |
|---|---|---|
value |
this.progress |
当前进度值(0-100) |
total |
100 | 最大值 |
type |
ProgressType.Ring |
环形样式 |
style.strokeWidth |
18 | 环的粗细(px) |
color |
‘#4CAF50’ | 进度条颜色(绿色) |
Stack 组合技巧:将 Progress 和 Column 放在同一个 Stack 中,Column 位于 Progress 之上(后声明),通过居中对齐实现步数数字显示在环形中心的效果。这种「环形外圈 + 数据内芯」的设计模式在智能手表表盘和健康应用中被广泛使用。
3.4 条件渲染的按钮
if (!this.isWalking) {
Button() {
Text('▶️ 开始走路').fontSize(18).fontColor('#fff')
}.width(140).height(48).borderRadius(24).backgroundColor('#4CAF50')
.onClick(() => this.startWalking())
} else {
Button() {
Text('⏸️ 暂停').fontSize(18).fontColor('#fff')
}.width(140).height(48).borderRadius(24).backgroundColor('#FF9800')
.onClick(() => this.stopWalking())
}
与第 32 期的间隔计时器类似,这里也使用了条件渲染来切换按钮。但有一个细微差异:这里使用了 Button 的尾随 lambda 语法(Button() { Text(...) }),而不是简写形式(Button('文本'))。尾随 lambda 允许在按钮内部嵌套复杂的子组件布局,如同时显示图标和文字。
四、HarmonyOS 特性分析
4.1 Progress 组件的进阶用法
Progress.Ring 的 style 属性支持更丰富的定制:
.style({
strokeWidth: 18,
// strokeColor: Color.Grey, // 未完成部分的颜色(默认灰色)
// scaleCount: 60, // Scale 类型时的刻度数量
// scaleWidth: 4 // Scale 类型时的刻度宽度
})
当前代码使用了默认的灰色轨道 + 绿色进度,视觉效果清爽。如果要更精确地控制样式,可以为未完成部分指定 strokeColor。
4.2 高频定时器的性能考量
setInterval 间隔设置为 500ms(比常规的 1000ms 快一倍),这是为了模拟更自然的步频增长。高频定时器需要注意:
- 避免耗时操作:回调中只做简单的加法运算,不做网络请求或文件 I/O
- 合理的上限保护:
steps >= 99999自动停止 - 组数上限:步数最大不超过 99999,避免 UI 显示溢出
- 生命周期清理:
aboutToDisappear中清除定时器(虽然本页未显式声明,但应该补充)
4.3 toLocaleString 的本地化格式化
Text(this.steps.toLocaleString())
toLocaleString() 会根据系统语言环境自动添加千位分隔符。在中文环境下,10000 显示为 10,000,让大数字更易读。这是 JavaScript 内置 API,在 ArkTS 中完全可用。
五、UI/UX 设计与最佳实践
5.1 数据看板的布局策略
主界面分为四个信息层级:
- 第一层级:环形进度 + 步数(最醒目,视觉焦点)
- 第二层级:状态文字(还剩 X 步 / 达成目标)
- 第三层级:衍生数据(公里数、卡路里)
- 第四层级:操作按钮(控制模拟过程)
这种从「核心数据 → 状态提示 → 衍生数据 → 操作控制」的布局顺序,让用户先看到最重要的信息,再逐步了解细节,最后决定操作。
5.2 色彩与视觉设计
- 主色调:绿色(#4CAF50),象征健康、运动、活力
- 背景色:#f0faf0,极浅的绿色背景,与主题呼应又不喧宾夺主
- 数据卡片:白色背景 + 圆角,清爽简洁
- 按钮颜色:绿色(开始)、橙色(暂停)、灰色(重置),功能与颜色一一对应
5.3 防重复启动保护
if (this.isWalking) return;
startWalking() 的第一行就是防重复保护。如果用户快速多次点击「开始走路」按钮,只有第一次有效。这是幂等性设计的体现——同一个操作执行多次和执行一次的结果相同。
六、可扩展思路
- 真实传感器集成:使用
@ohos.sensor接入加速度传感器,实现真实计步而非模拟。 - 每日目标持久化:使用
@ohos.data.preferences保存每日步数和目标,显示周/月趋势图表。 - 成就系统:设定里程碑(如 10 万步、100 万步),达到后解锁徽章。
- 社交排行榜:接入云服务,与好友比拼步数。
- 运动模式:区分走路/跑步模式,不同模式使用不同的步幅和卡路里算法。
七、总结
计步器应用以 112 行代码,构建了一个完整的运动数据看板。它的技术亮点包括:
- Progress.Ring 环形进度条:在健康类应用中大放异彩的组件
- 高频数据模拟:500ms 间隔 + 随机增量,模拟真实步频
- 多维度数据衍生:从步数原始值衍生出进度、距离、卡路里
- Stack 环形中心组合:环形外圈 + 数据内芯的通用设计模式
- 条件渲染的按钮切换:同一位置不同状态的按钮展示
这个应用的架构模式——环形进度 + 中心数据 + 衍生统计——可以直接复用到卡路里追踪、睡眠监测、屏幕使用时间等各类数据看板类应用中。
下一期我们将从身体健康转向情绪健康,探讨一个充满温暖的设计——情绪追踪(Mood Tracker),看如何用 emoji 表情和 ForEach 循环构建情绪记录系统。
本文是 HarmonyOS 应用开发实战系列第 34 期,更多内容请关注专栏。
更多推荐


所有评论(0)