HarmonyOS7 ArkUI 实战:Progress 线性、圆环、胶囊与刻度进度条

发布时间:2026/7/15 5:38:37

HarmonyOS7 ArkUI 实战:Progress 线性、圆环、胶囊与刻度进度条
文章目录前言代码讲解页面入口怎么安排数据模型先稳住状态变量控制页面反馈布局代码不要从样式开始读Builder 和方法承担复用使用方式完整代码关键代码解析interface 不是摆设State 是交互的开关列表和卡片要靠数据驱动事件回调要短一点样式参数别急着抽常量总结前言进度条不能只是一条线。配合任务列表、颜色和文案用户才知道当前状态。我建议把这篇当成一个可以直接改造的 ArkUI 页面模板来看。先跑起来确认页面展示和交互都正常再把里面的模拟数据替换成接口数据最后再调整颜色、间距和业务字段。这样改起来比较稳不容易一边改样式一边把状态逻辑弄乱。这篇文章会按「页面意图、数据结构、状态流转、布局组织、交互细节」这条线来读代码。你不用从第一行样式硬看到最后先抓住主线后面的细节会轻松很多。代码讲解页面入口怎么安排代码里的入口组件是ProgressStylesShowcasePage。如果你把它当成单独页面预览保留Entry就可以如果要塞进现有工程的路由体系通常会去掉Entry再由外层页面或路由模块统一管理。这个习惯很重要。一个项目里入口页面太多预览时容易混后期拆组件也会麻烦。我的做法是案例阶段保留入口接业务时只保留真正需要被路由访问的页面。数据模型先稳住本案例涉及的数据模型主要有TaskProgress。这些interface的作用不是为了“看起来规范”而是给 UI 层划边界。比如列表项需要标题、描述、颜色、选中态就应该在模型里说清楚。后续从接口拿数据时可以在请求层做字段映射别让后端字段名直接污染页面代码。如果你准备接真实业务建议把模拟数组替换成接口返回后的 ViewModel。页面只关心自己要渲染什么不关心接口原始字段长什么样。状态变量控制页面反馈本案例里的关键状态包括overallProgress、ringProgress、isAnimating、taskList。State的职责是让 UI 对用户操作有反馈。比如选中某个卡片、切换分类、展开内容、修改输入值、控制加载中状态这些都应该由状态驱动。状态一变相关 UI 自动刷新这也是 ArkUI 声明式开发最核心的体验。这里有个容易踩的坑不要把所有数据都塞进State。静态配置用普通数组就够了只有用户会改、接口会更新、页面需要重新渲染的数据才适合放进状态里。布局代码不要从样式开始读这个页面里比较关键的组件有Row横向排列、左右分栏、按钮组或卡片行都依赖它来控制水平方向的节奏。Column纵向组织标题、内容、操作区适合搭页面主骨架。Stack负责图层叠放常见于角标、遮罩、封面文字压图。Scroll用于长内容容器锚点跳转和返回顶部都离不开滚动控制。Button按钮案例要关注 loading、disabled、主题色和点击反馈。Progress进度条最好和文字、颜色、任务状态一起使用。读布局时我更建议先找大容器再看内部如何拆块。比如一个页面通常会先用Column纵向放标题区、内容区、操作区内容区里再用Row、Grid、List或其他容器细分。这样看代码层级会比逐行看.padding()、.fontSize()清楚得多。如果页面显示错位优先检查容器关系而不是马上改像素。很多问题不是某个.margin()不对而是外层容器的宽高、滚动方向、权重分配或对齐方式没想清楚。Builder 和方法承担复用这篇没有额外拆出 Builder页面结构主要集中在build()中。辅助方法主要有increaseProgress、resetProgress、getProgressLabel、getProgressColor。Builder更适合复用 UI 片段比如卡片、列表行、头部区域、按钮组。普通方法更适合放计算逻辑比如筛选列表、统计数量、切换状态、生成颜色。两者分清楚后页面会干净很多。我不建议把所有东西都塞进build()。刚开始代码少还行需求一多build()会变成几百行后面改一个按钮都要找半天。使用方式把下面的完整代码放到 ArkTS 页面文件里即可运行。文章中的组件名已经是语义化命名不包含编号式案例名称接路由时直接使用ProgressStylesShowcasePage就行。如果你要改成业务页面优先改三处数据模型、状态变量、事件方法。样式可以后调先保证数据流和交互是通的。完整代码// ProgressStylesShowcasePage: 进度条组件 - Progress 各种样式 // 知识点: Progress、ProgressStyle、Linear/Ring/Eclipse/ScaleRing/Capsule、动态更新 interface TaskProgress { taskId: number taskName: string taskColor: string taskBg: string progressValue: number taskEmoji: string } Entry Component struct ProgressStylesShowcasePage { State overallProgress: number 65 State ringProgress: number 75 State isAnimating: boolean false State taskList: TaskProgress[] [ { taskId: 1, taskName: 需求分析, taskColor: #3B82F6, taskBg: #EDF4FF, progressValue: 100, taskEmoji: }, { taskId: 2, taskName: UI 设计, taskColor: #10B981, taskBg: #F0FFF4, progressValue: 85, taskEmoji: }, { taskId: 3, taskName: 前端开发, taskColor: #F59E0B, taskBg: #FFFBF0, progressValue: 60, taskEmoji: }, { taskId: 4, taskName: 后端开发, taskColor: #8B5CF6, taskBg: #FDF0FF, progressValue: 45, taskEmoji: ⚙️ }, { taskId: 5, taskName: 测试验收, taskColor: #EF4444, taskBg: #FFF0F0, progressValue: 20, taskEmoji: }, { taskId: 6, taskName: 上线发布, taskColor: #EC4899, taskBg: #FFF0F6, progressValue: 0, taskEmoji: }, ] increaseProgress() { if (this.overallProgress 100) { this.overallProgress Math.min(100, this.overallProgress 5) this.ringProgress Math.min(100, this.ringProgress 5) } } resetProgress() { this.overallProgress 65 this.ringProgress 75 } getProgressLabel(val: number): string { if (val 100) return 已完成 ✓ if (val 80) return 接近完成 if (val 50) return 进行中 if (val 0) return 刚开始 return 未开始 } getProgressColor(val: number): string { if (val 100) return #10B981 if (val 60) return #F59E0B if (val 0) return #3B82F6 return #E0E0E0 } build() { Column({ space: 0 }) { // 标题栏 Row({ space: 0 }) { Text(Progress 进度条) .fontSize(18) .fontWeight(FontWeight.Bold) .fontColor(#1A1A1A) .layoutWeight(1) Text(总进度 ${this.overallProgress}%) .fontSize(13) .fontColor(#007DFF) .fontWeight(FontWeight.Bold) } .width(100%) .height(52) .padding({ left: 16, right: 16 }) .backgroundColor(#FFFFFF) .border({ width: { bottom: 1 }, color: #F0F0F0 }) Scroll() { Column({ space: 12 }) { // 一、Linear 线性进度条 Column({ space: 10 }) { Text(一、Linear 线性进度条) .fontSize(15) .fontWeight(FontWeight.Bold) .fontColor(#1A1A1A) .width(100%) ForEach(this.taskList, (task: TaskProgress) { Column({ space: 6 }) { Row({ space: 0 }) { Text(task.taskEmoji) .fontSize(16) .margin({ right: 8 }) Text(task.taskName) .fontSize(14) .fontColor(#1A1A1A) .fontWeight(FontWeight.Medium) .layoutWeight(1) Text(this.getProgressLabel(task.progressValue)) .fontSize(12) .fontColor(this.getProgressColor(task.progressValue)) Text( ${task.progressValue}%) .fontSize(13) .fontColor(task.taskColor) .fontWeight(FontWeight.Bold) } .width(100%) .alignItems(VerticalAlign.Center) Progress({ value: task.progressValue, total: 100, type: ProgressType.Linear }) .width(100%) .height(8) .color(task.taskColor) .style({ strokeWidth: 8, enableSmoothEffect: true }) } .width(100%) }, (task: TaskProgress) task.taskId.toString()) } .width(100%) .padding(16) .backgroundColor(#FFFFFF) .borderRadius(12) // 二、Ring 圆形进度条 Column({ space: 12 }) { Text(二、Ring 圆环进度条) .fontSize(15) .fontWeight(FontWeight.Bold) .fontColor(#1A1A1A) .width(100%) Row({ space: 0 }) { // 大圆环 - 整体进度 Column({ space: 8 }) { Stack({ alignContent: Alignment.Center }) { Progress({ value: this.ringProgress, total: 100, type: ProgressType.Ring }) .width(120) .height(120) .color(#007DFF) .style({ strokeWidth: 12, enableSmoothEffect: true }) Column({ space: 2 }) { Text(${this.ringProgress}%) .fontSize(22) .fontWeight(FontWeight.Bold) .fontColor(#1A1A1A) Text(完成) .fontSize(12) .fontColor(#888888) } } Text(项目总体进度) .fontSize(13) .fontColor(#555555) } .layoutWeight(1) .alignItems(HorizontalAlign.Center) // 小圆环组合 Column({ space: 10 }) { Row({ space: 16 }) { Column({ space: 4 }) { Stack({ alignContent: Alignment.Center }) { Progress({ value: 92, total: 100, type: ProgressType.Ring }) .width(64) .height(64) .color(#10B981) .style({ strokeWidth: 7 }) Text(92%).fontSize(12).fontColor(#10B981).fontWeight(FontWeight.Bold) } Text(代码质量).fontSize(11).fontColor(#888888) } .alignItems(HorizontalAlign.Center) Column({ space: 4 }) { Stack({ alignContent: Alignment.Center }) { Progress({ value: 78, total: 100, type: ProgressType.Ring }) .width(64) .height(64) .color(#F59E0B) .style({ strokeWidth: 7 }) Text(78%).fontSize(12).fontColor(#F59E0B).fontWeight(FontWeight.Bold) } Text(测试覆盖).fontSize(11).fontColor(#888888) } .alignItems(HorizontalAlign.Center) } Row({ space: 16 }) { Column({ space: 4 }) { Stack({ alignContent: Alignment.Center }) { Progress({ value: 55, total: 100, type: ProgressType.Ring }) .width(64) .height(64) .color(#8B5CF6) .style({ strokeWidth: 7 }) Text(55%).fontSize(12).fontColor(#8B5CF6).fontWeight(FontWeight.Bold) } Text(文档完整).fontSize(11).fontColor(#888888) } .alignItems(HorizontalAlign.Center) Column({ space: 4 }) { Stack({ alignContent: Alignment.Center }) { Progress({ value: 40, total: 100, type: ProgressType.Ring }) .width(64) .height(64) .color(#EF4444) .style({ strokeWidth: 7 }) Text(40%).fontSize(12).fontColor(#EF4444).fontWeight(FontWeight.Bold) } Text(性能优化).fontSize(11).fontColor(#888888) } .alignItems(HorizontalAlign.Center) } } .layoutWeight(1) } .width(100%) .alignItems(VerticalAlign.Center) } .width(100%) .padding(16) .backgroundColor(#FFFFFF) .borderRadius(12) // 三、Capsule/Eclipse 样式 Column({ space: 10 }) { Text(三、Capsule 胶囊 Eclipse 圆球样式) .fontSize(15) .fontWeight(FontWeight.Bold) .fontColor(#1A1A1A) .width(100%) Column({ space: 8 }) { Text(Capsule 样式两端为圆弧) .fontSize(13) .fontColor(#888888) Progress({ value: this.overallProgress, total: 100, type: ProgressType.Capsule }) .width(100%) .height(28) .color(#3B82F6) .style({ strokeWidth: 28, enableSmoothEffect: true }) } .width(100%) Column({ space: 8 }) { Text(ScaleRing 刻度环形样式) .fontSize(13) .fontColor(#888888) Stack({ alignContent: Alignment.Center }) { Progress({ value: this.overallProgress, total: 100, type: ProgressType.ScaleRing }) .width(100) .height(100) .color(#F59E0B) .style({ strokeWidth: 20, scaleCount: 20, scaleWidth: 3 }) Text(${this.overallProgress}%) .fontSize(18) .fontWeight(FontWeight.Bold) .fontColor(#F59E0B) } } .width(100%) .alignItems(HorizontalAlign.Center) } .width(100%) .padding(16) .backgroundColor(#FFFFFF) .borderRadius(12) // 四、动态控制 Column({ space: 10 }) { Text(四、动态更新进度) .fontSize(15) .fontWeight(FontWeight.Bold) .fontColor(#1A1A1A) .width(100%) Row({ space: 12 }) { Button(5%) .type(ButtonType.Capsule) .layoutWeight(1) .height(44) .fontSize(15) .backgroundColor(#007DFF) .onClick(() { this.increaseProgress() }) Button(重置) .type(ButtonType.Capsule) .layoutWeight(1) .height(44) .fontSize(15) .backgroundColor(#F5F5F5) .fontColor(#555555) .onClick(() { this.resetProgress() }) } .width(100%) } .width(100%) .padding(16) .backgroundColor(#FFFFFF) .borderRadius(12) } .padding({ left: 12, right: 12, top: 12, bottom: 24 }) } .scrollBar(BarState.Off) .layoutWeight(1) } .width(100%) .height(100%) .backgroundColor(#F5F6FA) } }关键代码解析interface不是摆设很多新手写 ArkUI 页面时会直接在ForEach里使用一堆临时对象。页面小的时候没问题但字段一多就很难知道每个字段到底用于哪里。本案例把数据结构提前声明出来读代码时能很快判断每个 UI 区块依赖哪些字段。后续如果接接口也建议保留这层结构。接口可以变页面模型尽量稳定。这样页面不会因为后端多返回一个字段、少返回一个字段就跟着大改。State是交互的开关本案例的交互反馈都围绕状态展开。点击、切换、输入、刷新这类行为本质上都是修改某个状态值然后让 UI 重新计算显示结果。写这类代码时有一个简单判断如果某个值变化后页面应该立刻变那它大概率应该是状态如果它只是配置项、静态文案、固定颜色就不要放进状态。列表和卡片要靠数据驱动只要页面里出现重复结构就应该优先想到数据数组加ForEach。这样新增一项、删除一项、调整顺序都只动数据不用复制粘贴一段 UI。这个案例的完整代码里已经把主要内容抽成数组或方法。你可以试着多加一条数据看页面是否能自然渲染出来。如果能说明结构是健康的如果加一条就要改很多 UI说明组件拆分还不够。事件回调要短一点.onClick()、onChange()这类回调里可以直接改状态但不建议堆太多逻辑。简单切换可以写在回调里复杂逻辑最好拆成方法。这样做有两个好处一是读页面结构时不会被业务逻辑打断二是后面要加埋点、请求接口、异常处理时有明确的位置可以改。样式参数别急着抽常量这个案例里有不少颜色、间距、圆角、字号。学习阶段直接写在组件链上反而更容易看出效果。等你确定页面风格稳定后再把主题色、通用间距、卡片圆角提到统一常量里。过早抽象会让示例代码变绕。先让页面清楚再谈工程化这是我比较推荐的顺序。总结这个案例真正值得学的不只是某个 ArkUI 组件怎么写而是一个页面如何从数据走到状态再走到布局和交互。你可以按这个顺序改造先替换数据模型再确认状态更新是否正确接着调整布局容器最后打磨样式细节。只要这条主线不乱页面规模变大也不会失控。如果后续继续扩展我会优先把可复用的卡片、列表行、筛选栏或控制区拆成独立组件。这样主页面只保留数据和流程代码会更接近真实项目里的写法。

相关新闻

从零实现C++矩阵类:深入理解内存管理、运算符重载与数值计算

从零实现C++矩阵类:深入理解内存管理、运算符重载与数值计算

2026/7/15 5:38:37

1. 项目概述:为什么从零构建一个Matrix类?在C的世界里,处理数学运算,尤其是线性代数中的矩阵运算,是图形学、机器学习、物理仿真等领域的家常便饭。你可能用过Eigen、OpenCV的Mat,或者Armadillo这些成熟的库…

C#调用C++动态库的七种场景与实战解决方案

C#调用C++动态库的七种场景与实战解决方案

2026/7/15 5:38:37

1. 项目概述:为什么C#开发者绕不开C动态库?在桌面应用、工业上位机、游戏开发乃至金融交易系统的构建中,C#凭借其优雅的语法、强大的.NET生态和高效的开发效率,成为了许多开发者的首选。然而,当你需要处理高性能计算、…

端到端实时语音同传系统:3秒延迟与0样本声音复刻技术解析

端到端实时语音同传系统:3秒延迟与0样本声音复刻技术解析

2026/7/15 5:38:37

1. 这不是“翻译软件”,而是一套实时语音交互操作系统我第一次在中关村论坛后台听到豆包同声传译2.0的实测音频时,下意识摸了摸耳机——不是因为音质多震撼,而是它太“不像AI”了。一位中文发言嘉宾语速偏快、夹杂两个专业术语、中间还停顿半…

【Java 8 新特性】Java LocalDate 实战:从基础操作到高级应用

【Java 8 新特性】Java LocalDate 实战:从基础操作到高级应用

2026/7/15 7:08:41

1. LocalDate基础入门LocalDate是Java 8中引入的一个不可变日期类,专门用于处理不带时间的纯日期(格式为yyyy-MM-dd)。它彻底解决了传统Date和Calendar类的设计缺陷,比如线程安全问题、月份从0开始等反人类设计。创建LocalDate对象…

重磅发布!用Scratch复刻《我的世界》:从零到一的编程探险

重磅发布!用Scratch复刻《我的世界》:从零到一的编程探险

2026/7/15 7:08:41

1. 为什么用Scratch复刻《我的世界》?十年前我第一次接触Scratch时,完全没想到这个拖拽积木的编程工具能做出像《我的世界》(MC)这样复杂的游戏。直到看到国外一个12岁孩子用Scratch做的3D迷宫,我才意识到图形化编程的…

攀枝花3路公交:穿越北岸的老资历线路运营解析

攀枝花3路公交:穿越北岸的老资历线路运营解析

2026/7/15 7:08:41

攀枝花公交3路:穿越偏僻北岸的老资历线路全解析在攀枝花这座山城,公交线路如同城市的血脉,连接着各个区域。其中3路公交作为一条"老资历"线路,以其几乎未改过线的稳定性和穿越偏僻北岸的独特路线,成为了解这…

Windows系统如何获取苹果苹方字体:从字体原理到实战应用的完整指南

Windows系统如何获取苹果苹方字体:从字体原理到实战应用的完整指南

2026/7/15 7:08:41

Windows系统如何获取苹果苹方字体:从字体原理到实战应用的完整指南 【免费下载链接】PingFangSC PingFangSC字体包文件、苹果平方字体文件,包含ttf和woff2格式 项目地址: https://gitcode.com/gh_mirrors/pi/PingFangSC 你是否厌倦了Windows系统默…

基于文件共享见证的故障转移集群高可用文件服务器部署指南

基于文件共享见证的故障转移集群高可用文件服务器部署指南

2026/7/15 7:08:40

1. 文件共享见证的核心价值在传统的高可用方案中,磁盘见证(Disk Witness)是最常见的仲裁资源配置方式。但实际运维中经常会遇到这样的困境:当企业没有共享存储设备时,传统的磁盘见证方案就难以实施。这正是文件共享见证…

eino框架中AI推理过程透明化的架构设计与实现

eino框架中AI推理过程透明化的架构设计与实现

2026/7/15 6:58:40

eino框架中AI推理过程透明化的架构设计与实现 【免费下载链接】eino The ultimate LLM/AI application development framework in Go. 项目地址: https://gitcode.com/GitHub_Trending/ei/eino 在构建现代AI应用时,模型透明性已成为开发者关注的核心议题。ei…

Unity游戏文本翻译架构深度解析:XUnity.AutoTranslator的技术实现与工程实践

Unity游戏文本翻译架构深度解析:XUnity.AutoTranslator的技术实现与工程实践

2026/7/14 10:03:09

Unity游戏文本翻译架构深度解析:XUnity.AutoTranslator的技术实现与工程实践 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator XUnity.AutoTranslator作为Unity游戏社区中最成熟的文本翻译解决方…

openEuler Raspberry Pi Kernel设备驱动开发指南:为树莓派硬件添加支持

openEuler Raspberry Pi Kernel设备驱动开发指南:为树莓派硬件添加支持

2026/7/13 20:43:19

openEuler Raspberry Pi Kernel设备驱动开发指南:为树莓派硬件添加支持 【免费下载链接】raspberrypi-kernel It provides openEuler kernel source for Raspberry Pi 项目地址: https://gitcode.com/openeuler/raspberrypi-kernel 前往项目官网免费下载&…

openEuler系统集成测试实战:基于smoke-test套件的环境验证技巧

openEuler系统集成测试实战:基于smoke-test套件的环境验证技巧

2026/7/15 0:26:43

openEuler系统集成测试实战:基于smoke-test套件的环境验证技巧 【免费下载链接】integration-test The repo contains test suits for system integration test 项目地址: https://gitcode.com/openeuler/integration-test 前往项目官网免费下载:…

【LINUX】驱动

【LINUX】驱动

2026/7/15 0:08:14

【LINUX驱动】【字符设备】【中断】【Platform】【网课 设备树】【GPIO】【PINCTRL】【INPUT】【IIC】【SPI】【网络驱动】【屏幕驱动】【一 设备树】【二 内核模块编译】【三 基本驱动框架】【四 Platform总线设备驱动框架】【五 驱动子系统】【六 综合】

【1982-2026】全国高精度建筑轮廓|村级精度|SHP矢量

【1982-2026】全国高精度建筑轮廓|村级精度|SHP矢量

2026/7/15 0:08:14

🔍 数据简介 本次分享1982-2026年全国村级精度建筑轮廓矢量数据,覆盖全国各省市区县,到村级别精细,为2026年最新实时采集成果,非网传仅60/77个城市的老旧数据。 数据含带高度/不带高度双版本,单体建筑边界精…

【1975-2026】全国水系水路数据|河流/水库/运河|SHP矢量

【1975-2026】全国水系水路数据|河流/水库/运河|SHP矢量

2026/7/15 0:08:14

🔍 数据简介 本次分享1975-2026年全国高精度水系水路矢量数据,覆盖全国全域,包含河流、水系、水库、运河、湿地、冰川、沟渠等全类别水文要素。 数据集包含双层矢量图层,字段分类清晰、要素齐全,支持2013-2026逐年完整…