鸿蒙新特性——Progress 进度条组件详解
一、引言
进度条是移动端 UI 中最基础的视觉元素之一。从 App 安装进度到任务完成率,从运动目标的环形进度到数据加载的胶囊条——进度条无处不在。在 HarmonyOS NEXT 之前,开发者要实现进度条效果,通常需要 Stack 叠加两个不同颜色的矩形手动绘制,或者引入第三方 Canvas 库——不仅代码冗长,而且动画、颜色、圆角等细节难以统一。
ArkUI 在 API 10 中新增了 Progress 组件,将进度条的四种形态统一封装:线性(Linear)、环形(Ring)、胶囊(Capsule) 和 刻度环(ScaleRing)。一个组件,四种形态,统一的属性和颜色配置——开发者只需要声明组件类型和数值,就能获得完整的进度展示效果。
本文通过一个健身目标追踪 Demo深入讲解 Progress 组件的四种类型和核心用法:如何用 Linear 展示任务完成比例?环形进度和刻度环进度的区别是什么?如何用 setInterval 驱动进度条的动态变化?以及如何在实际业务场景中组合多种进度类型。
阅读完本文,你将能够:
- 使用 Progress 组件的四种类型(Linear、Ring、Capsule、ScaleRing)
- 通过
value和total控制进度比例 - 通过
color和backgroundColor自定义进度条的视觉风格 - 通过
style({ strokeWidth })控制线条粗细 - 用定时器实现进度条动画,以及正确的资源清理方式
二、Progress 组件 API 总览
2.1 构造函数参数
Progress({ value: 0, total: 100, type: ProgressType.Linear })
| 参数 | 类型 | 说明 |
|---|---|---|
value |
number |
当前进度值,范围 0 到 total。组件根据 value / total 的比例渲染填充区域 |
total |
number |
进度总值,默认为 100。通常设为目标的完整数值(如 10000 步、500 卡路里) |
type |
ProgressType |
进度条类型:Linear(线性)、Ring(环形)、Capsule(胶囊)、ScaleRing(刻度环) |
value 和 total 的关系决定了填充比例。当 value >= total 时,进度条完全填满。Progress 组件本身不维护内部状态——它始终反映外部传入的 value 和 total。要实现动态进度变化,开发者需要在外部更新 value 值。
2.2 属性方法
color(value: ResourceColor)
设置进度条的填充颜色(即"已完成"部分的颜色):
Progress({ value: 70, total: 100, type: ProgressType.Linear })
.color('#1677FF') // 蓝色填充
这是 Progress 最常用的属性。颜色应与业务语义匹配:健身类用蓝色或绿色(积极、活力),警告类用橙色或红色(注意、紧迫),数据展示类用品牌色。
backgroundColor(value: ResourceColor)
设置进度条的背景颜色(即"未完成"部分的颜色):
.backgroundColor('#E8E8EE') // 浅灰色背景
背景色的选择有两个要点:一是与填充色有足够对比度(否则进度边界不清晰),二是不能太显眼(否则喧宾夺主)。浅灰色(#E8E8EE 或 #F0F0F5)是大多数场景的通用选择。
style(value: ProgressStyleOptions)
设置进度条的样式参数,目前主要通过 strokeWidth 控制线条粗细:
.style({ strokeWidth: 8 }) // 线性/Capsule 的线条高度或 Ring/ScaleRing 的环宽度
strokeWidth 的行为因类型而异:
- Linear / Capsule:决定进度条的高度(即填充区域的垂直厚度)。推荐 6-12vp。
- Ring / ScaleRing:决定环形进度的"环宽度"(即圆环的线条粗细)。推荐 4-8vp——太粗显得笨重,太细不够醒目。
2.3 ProgressType 四种类型对比
| 类型 | 形状 | 适用场景 | 典型尺寸 |
|---|---|---|---|
Linear |
水平长条 | 任务完成度、文件上传、下载进度 | width: 120-200, height: 6-10 |
Ring |
封闭圆环 | 单目标的百分比展示(如存储空间、电量剩余) | 40-80 × 40-80 |
Capsule |
水平胶囊(两端圆角) | 与 Linear 类似,但视觉更柔和 | width: 120-200, height: 6-10 |
ScaleRing |
带刻度标记的环形 | 需要精确刻度感的场景(如仪表盘、评分环) | 40-80 × 40-80 |
Linear 和 Capsule 在功能上几乎相同,区别仅在于视觉:Capsule 的端点更圆润,适合"柔和的 UI 风格";Linear 端点平直,适合"干练的 UI 风格"。在实际项目中,两者使用场景没有严格界限,按 UI 设计偏好选择即可。
Ring 和 ScaleRing 则差异更大:
- Ring 是一个连续平滑的圆环,填充区域从 12 点钟方向顺时针增长。
- ScaleRing 在 Ring 的基础上增加了刻度标记——环被均匀分割为若干段,每段之间有微小间隙。这种刻度感让进度看起来更像"仪表盘上的指针读数"。
- ScaleRing 的刻度数量由
total隐式决定——当total=30时,环上有 30 个刻度标记。
2.4 尺寸控制
Progress 的尺寸通过通用属性 width 和 height 控制:
// 线性/胶囊:宽 180vp,高 8vp
Progress({ value: 70, total: 100, type: ProgressType.Linear })
.width(180)
.height(8)
// 环形/刻度环:宽高相等,60×60vp
Progress({ value: 70, total: 100, type: ProgressType.Ring })
.width(60)
.height(60)
对于 Ring 和 ScaleRing,width 和 height 应设置为相等值(正方形区域),否则圆环会被拉伸变形。
三、Demo 设计:健身目标追踪器
3.1 功能概述
Demo 模拟一个健身目标追踪面板,展示 4 个健身指标,每个指标使用一种 Progress 类型:
| 指标 | Progress 类型 | 目标 | 颜色 |
|---|---|---|---|
| 今日步数 | Linear | 10000 步 | #1677FF 蓝 |
| 卡路里消耗 | Ring | 500 千卡 | #FF9800 橙 |
| 本周运动天数 | Capsule | 7 天 | #52C41A 绿 |
| 月度健身目标 | ScaleRing | 30 天 | #9C27B0 紫 |
点击"开始模拟"按钮后,4 个进度条同时从 0 开始增长,通过 setInterval 每 80ms 递增一次。当所有指标达到目标值时,动画自动停止。再次点击按钮可重新开始模拟。
3.2 四种 Progress 类型的实现
Linear 线性进度条(今日步数):
Progress({ value: this.steps, total: this.stepTarget, type: ProgressType.Linear })
.width(180)
.height(8)
.color('#1677FF')
.backgroundColor('#E8E8EE')
.style({ strokeWidth: 8 })
Linear 是最直观的进度条类型——一个水平长条从左侧开始填充。width(180) 配合 style({ strokeWidth: 8 }) 让进度条有足够的水平空间显示 0 到 10000 步的增长过程。蓝色(#1677FF)传达"数据追踪"的冷静感——步数是一个客观数据指标。
Ring 环形进度条(卡路里消耗):
Progress({ value: this.calories, total: this.calTarget, type: ProgressType.Ring })
.width(60)
.height(60)
.color('#FF9800')
.backgroundColor('#E8E8EE')
.style({ strokeWidth: 6 })
Ring 是经典的"圆环进度"——从 12 点钟方向顺时针填充。60×60vp 的正方形区域提供了紧凑但醒目的展示。橙色(#FF9800)与卡路里的"热量"语义一致——温暖、活跃。strokeWidth(6) 让环线有一定厚度,在手机屏幕上清晰可见。
Capsule 胶囊进度条(本周运动天数):
Progress({ value: this.workDays, total: this.workTarget, type: ProgressType.Capsule })
.width(180)
.height(8)
.color('#52C41A')
.backgroundColor('#E8E8EE')
.style({ strokeWidth: 8 })
Capsule 在功能上与 Linear 相同,但在视觉上两端呈圆角。这种柔和的视觉风格适合"生活类"指标——运动天数与步数不同,它更偏向"习惯养成"而非"数据记录"。绿色(#52C41A)传达健康和成就。
ScaleRing 刻度环进度条(月度健身目标):
Progress({ value: this.monthDays, total: this.monthTarget, type: ProgressType.ScaleRing })
.width(60)
.height(60)
.color('#9C27B0')
.backgroundColor('#E8E8EE')
.style({ strokeWidth: 6 })
ScaleRing 在 Ring 的基础上增加了刻度分割——30 个刻度对应 30 天。当 monthDays=22 时,环上有 22 个亮色段和 8 个暗色段,每个段代表一天。紫色(#9C27B0)传达"高级/仪式感"——月度目标是一个更长周期的承诺,比每日步数更有分量。
3.3 进度动画与定时器管理
Demo 的核心交互是"模拟进度"——点击按钮后,4 个指标同时从 0 开始递增,直到全部达到目标。动画使用 setInterval 驱动:
private animTimer: number = -1;
simulateProgress(): void {
if (this.isAnimating) {
// 停止动画
if (this.animTimer !== -1) {
clearInterval(this.animTimer);
this.animTimer = -1;
}
this.isAnimating = false;
return;
}
this.isAnimating = true;
// 重置为 0
this.steps = 0;
this.calories = 0;
this.workDays = 0;
this.monthDays = 0;
this.animTimer = setInterval(() => {
if (this.steps < this.stepTarget) {
this.steps = Math.min(this.steps + 250, this.stepTarget);
}
if (this.calories < this.calTarget) {
this.calories = Math.min(this.calories + 15, this.calTarget);
}
if (this.workDays < this.workTarget) {
this.workDays = Math.min(this.workDays + 1, this.workTarget);
}
if (this.monthDays < this.monthTarget) {
this.monthDays = Math.min(this.monthDays + 1, this.monthTarget);
}
// 全部达标则停止
if (this.steps >= this.stepTarget && this.calories >= this.calTarget &&
this.workDays >= this.workTarget && this.monthDays >= this.monthTarget) {
clearInterval(this.animTimer);
this.animTimer = -1;
this.isAnimating = false;
}
}, 80);
}
这里有几个设计要点:
1. 不同指标的递增速度不同
步数每次 +250,卡路里每次 +15,天数额每次 +1。因为目标值不同(10000 vs 500 vs 7 vs 30),递增速度也相应调整,使得 4 个进度条的填充速度在视觉上保持接近。如果都用相同步长,步数需要 40 帧才能填满而天数只需 7 帧——用户会看到天数进度条一闪而过。
2. Math.min 防止溢出
每次递增后使用 Math.min(current + delta, target) 确保最终值恰好等于目标,而不是 0, 250, 500, ..., 9750, 10000, 10250。最后一帧可能超出目标值,Math.min 将其裁剪到精确目标。
3. 80ms 间隔的选择
80ms 约等于 12.5 fps——足够流畅但不消耗过多 CPU。如果设为 16ms(60fps),会导致状态更新频率过高,在某些低端设备上可能出现卡顿。80ms 是一个在流畅性和性能之间的平衡选择。
4. 定时器 ID 的存储与清理
aboutToDisappear(): void {
if (this.animTimer !== -1) {
clearInterval(this.animTimer);
this.animTimer = -1;
}
}
aboutToDisappear 是 ArkUI 的生命周期回调,在页面即将销毁时调用。必须在此时清理定时器——如果页面已经离开但定时器仍在运行,会导致:
- 无效的状态更新(操作已销毁的组件)
- CPU 资源浪费(定时器持续运行但无渲染目标)
- 内存泄漏(定时器回调持有对组件实例的引用)
animTimer 初始化为 -1 作为"无活动定时器"的哨兵值。每次清理后重置为 -1,确保不会重复清理。
3.4 百分比计算与数值展示
每个进度卡片除 Progress 组件外,还展示当前值和百分比:
percentStr(current: number, target: number): string {
return '' + Math.round(current / target * 100) + '%';
}
// 使用
Text('' + current + ' / ' + target)
.fontSize(14)
.fontColor('#1a1a2e')
.fontWeight(FontWeight.Bold)
.margin({ bottom: 2 })
Text(this.percentStr(current, target))
.fontSize(20)
.fontColor(color)
.fontWeight(FontWeight.Bold)
数值展示与 Progress 组件形成互补:Progress 提供视觉(“大约多少”),数字提供精确值(“具体多少/目标多少”),百分比提供比例感(“完成了百分之多少”)。三者共同构成完整的进度信息。
百分比文字使用与进度条相同的颜色(color 参数传入),形成色彩呼应。20sp 的字号比进度条值(14sp)更大,因为百分比是用户最关心的信息——"我完成了 85%"比"8500 / 10000"更直观。
3.5 页面结构
┌──────────────────────────────────────────┐
│ 📊 进度条组件(深色标题栏) │
├──────────────────────────────────────────┤
│ 📘 Progress 组件说明卡片 │
├──────────────────────────────────────────┤
│ ┌────────────────────────────────────┐ │
│ │ 👟 今日步数 Linear │ │
│ │ ████████████░░░░░░ 8500/10000 85%│ │
│ └────────────────────────────────────┘ │
│ ┌────────────────────────────────────┐ │
│ │ 🔥 卡路里消耗 Ring │ │
│ │ ◯ 420/500 84% │ │
│ └────────────────────────────────────┘ │
│ ┌────────────────────────────────────┐ │
│ │ 📅 本周运动天数 Capsule │ │
│ │ ████████░░░░░░ 5/7 71% │ │
│ └────────────────────────────────────┘ │
│ ┌────────────────────────────────────┐ │
│ │ 🎯 月度健身目标 ScaleRing │ │
│ │ ◯ 22/30 73% │ │
│ └────────────────────────────────────┘ │
├──────────────────────────────────────────┤
│ [▶ 开始模拟] / [⏹ 停止模拟] │
└──────────────────────────────────────────┘
布局为 Scroll 内嵌 Column,整体浅灰背景(#F2F3F5),卡片白色背景(#FFFFFF)带圆角。每个进度卡片分为上下两部分:上方是标题行(指标名 + 类型标签),下方是进度区(Progress 组件 + 数值显示)。
类型标签使用彩色底色 + 白色文字的小标签,12sp 字号,与进度条颜色一致——这是一个小的视觉锚点,帮助用户快速区分四种类型。
四、Progress 组件的最佳实践
4.1 类型选择指南
Progress 四种类型的选择取决于展示空间和信息密度:
线性 / 胶囊(Linear / Capsule):
适合需要水平空间、信息密度较高的场景。线性进度条可以很窄(6-10vp 高),放在列表项或卡片中不占用太多垂直空间。典型场景:
- 任务列表中的每项完成度
- 文件下载/上传进度
- 表单填写步骤(第 3/5 步)
Linear 和 Capsule 的选择主要取决于 UI 风格:如果你的 App 使用大量圆角卡片和柔和线条,选 Capsule;如果 UI 风格偏扁平化和高效,选 Linear。
环形 / 刻度环(Ring / ScaleRing):
适合需要视觉强调、单独展示的场景。环形进度条占据正方形空间(至少 40×40vp),比线性进度条更"重"。典型场景:
- Dashboard 面板中的核心指标
- 个人主页的完成度(如资料填写 85%)
- 运动 App 的目标达成环
Ring 和 ScaleRing 的选择取决于是否需要刻度感:ScaleRing 的刻度标记增加了"精确读数"的感觉,适合仪表盘风格;Ring 更简洁,适合展示型 UI。如果 total 值较小(如 5、7、10),ScaleRing 的刻度会很稀疏,此时 Ring 可能更好。
4.2 颜色策略
Progress 的颜色应该服务于信息层级:
- 主色调(蓝、绿):用于常态指标——步数、完成率、进度。传达"一切正常,正在推进"。
- 警告色(橙、黄):用于有目标/截止日期的指标——卡路里剩余、存储空间不足。传达"注意,需要关注"。
- 强调色(紫、粉):用于特殊指标——月度目标、里程碑。传达"这个很重要,请特别关注"。
不要用红色(#FF0000)做 Progress 的填充色——红色在 UI 中通常意味着"错误/失败/停止",但进度条的本意是"已完成",两者语义冲突。
4.3 进度条与文字的配合
Progress 组件只提供视觉进度,不显示数字。在实际应用中,进度条至少应配一个百分比数字:
━━━━━━━━━━━━━━━━━━━━━ 85% ← 好(视觉 + 数字)
━━━━━━━━━━━━━━━━━━━━━ ← 差(只有视觉,不知道具体多少)
更进一步,推荐展示三个信息层级:
━━━━━━━━━━━━━━━━━━━━━ 8500 / 10000 步 ← 最佳(视觉 + 绝对值 + 目标)
85% ← 百分比作为强调
也可以用更紧凑的格式:
━━━━━━━━━━━━━━━━━━━━━ 8500 / 10000 (85%)
具体格式取决于布局空间。核心原则是:Progress 不能独立存在——它必须有数字同伴告诉用户"完成了多少"。
4.4 动画的使用
本 Demo 使用 setInterval 驱动动画是因为需要展示"进度从 0 增长到目标"的过程。但在实际业务中,进度条动画应谨慎使用:
- 下载/上传进度:不需要动画——直接显示真实进度值即可。真实进度本身就在变化,动画是多此一举。
- 加载骨架屏/占位进度:可以用 CSS 动画模拟不确定性进度(如从左到右的闪烁光条),但这不是 Progress 组件的场景。
- 结果展示页面(如"你完成了 85%"):可以用入场动画——页面加载后进度从 0 增长到 85%。这增加了仪式感和视觉趣味性。
如果要实现入场动画,推荐使用 ArkUI 的 animateTo 而非 setInterval——animateTo 是声明式的显式动画,性能更好且与组件生命周期自动绑定:
aboutToAppear(): void {
animateTo({ duration: 2000, curve: Curve.EaseOut }, () => {
this.steps = 8500;
});
}
但 animateTo 对 number 属性的动画需要组件重新渲染——而 Progress 组件的 value 变化本身会触发视觉变化,因此需要配合 @State 使用。本 Demo 使用 setInterval 而非 animateTo,因为需要同时控制 4 个指标的独立步长——animateTo 无法在动画过程中修改每个指标的递增速度。
4.5 无障碍与用户体验
Progress 组件在无障碍方面有两点需要注意:
-
颜色不是唯一的信息载体:不要只靠颜色区分"已完成"和"未完成"——进度条的形状(填充长度/角度)本身就传递了比例信息。红绿色盲用户可以靠长度判断进度,而不依赖颜色。
-
数值必须可见:如 4.3 节所述,始终配一个数字标签。这对所有用户都有用,对无法准确估计比例的用户(如视力不佳者)尤为重要。
五、完整代码结构
ProgressPage (~200 行)
├── 状态变量
│ ├── @State steps / calories / workDays / monthDays — 4 个指标的当前值
│ └── @State isAnimating — 动画状态(控制按钮文字)
├── 目标常量
│ └── stepTarget(10000) / calTarget(500) / workTarget(7) / monthTarget(30)
├── 生命周期
│ ├── aboutToAppear() — 初始化初始值(非零,展示静态效果)
│ └── aboutToDisappear() — 清理定时器
├── 业务逻辑
│ ├── simulateProgress() — 动画控制(开始/停止切换)
│ └── percentStr() — 百分比计算
├── 视图
│ ├── 标题栏 — 📊 进度条组件
│ ├── 说明卡片 — Progress 组件介绍
│ ├── 4 个 progressCard(@Builder)
│ │ └── 标题行 + 类型标签 + Progress 组件 + 数值 + 百分比
│ └── 开始/停止按钮
└── @Builder progressCard() — 可复用的进度卡片
└── if/else 根据 progressType 渲染不同 ProgressType
六、总结
本文通过一个健身目标追踪 Demo深入讲解了 HarmonyOS NEXT 中的 Progress 进度条组件。Progress 将四种常见进度形态(Linear、Ring、Capsule、ScaleRing)统一封装,通过 value/total 控制进度比例,通过 color/backgroundColor 控制视觉风格,通过 style({ strokeWidth }) 控制线条粗细。
核心要点回顾:
-
四种类型各有适用场景:Linear 和 Capsule 适合水平空间、列表项、任务条等线形场景;Ring 和 ScaleRing 适合需要视觉强调、单独展示的核心指标。Linear 偏扁平高效,Capsule 偏柔和;Ring 偏简洁,ScaleRing 偏仪表盘风格。
-
value/total 模式:Progress 不维护内部状态——它始终反映外部传入的
value和total值。这种"受控组件"模式意味着进度变化完全由开发者在外部控制——可以通过定时器、网络回调、用户操作等方式更新value。 -
颜色承载语义:蓝色的步数、橙色的卡路里、绿色的运动天数、紫色的月度目标——颜色不仅是装饰,更是信息。一种颜色绑定一种语义,用户不需要阅读文字就能通过颜色区分不同指标。
-
进度条不能独立存在:Progress 组件只提供视觉进度,必须配合数字显示(当前值 / 目标值 + 百分比)。视觉提供"大约感",数字提供"精确感",两者缺一不可。
-
定时器需要正确管理:
setInterval驱动动画时,必须在aboutToDisappear中清理,否则导致无效更新和内存泄漏。定时器 ID 使用初始值-1作为"无活动"的哨兵。
Progress 组件是 ArkUI 声明式组件体系中"一专多能"的典型——一个组件,四种形态,但每种形态都做到足够好。不需要引入第三方库,不需要手写 Canvas,不需要管理复杂的布局——Progress 让进度条的开发回归到"声明数值和颜色"这一本质。这种"用组件替代手工"的思路,正是 HarmonyOS NEXT 提升开发效率的核心策略。
七、扩展思考
Progress 组件的四种类型虽然已经覆盖了大多数进度展示场景,但在实际项目中,你可能还会遇到以下需求:
组合进度:多个进度条叠加展示——例如同时展示"步数进度"和"卡路里进度"在一个环形中。Progress 组件本身不支持叠加,但可以通过 Stack 将两个 Ring 进度条叠放实现——外层环是步数,内层环是卡路里。
渐变进度:填充色使用渐变色而非纯色。Progress 的 color 属性接受 ResourceColor,理论上可以通过自定义资源实现,但在实际项目中更常见的做法是用 Progress 作为底层占位,上层叠加一个渐变的半透明遮罩。
不确定进度:当无法估计完成时间时(如网络请求),使用不确定进度的动画(如左右扫描的光条)。ArkUI 的 Progress 组件在执行不确定进度动画方面功能有限——这种场景可能更适合使用 LoadingProgress 组件或自定义动画。
这些扩展场景提醒我们:Progress 是一个"基础组件",它解决了 80% 的进度展示需求。对于剩下的 20%,你可以基于 Progress 封装或完全自定义。但无论如何,理解 Progress 的核心机制(value/total、颜色映射、类型选择)是处理所有进度相关需求的基础。
更多推荐




所有评论(0)