文件下载、任务执行、数据加载——几乎所有需要等待的场景都需要进度指示。用户盯着一个静态的"加载中"文字时,每一秒都漫长;但如果看到一个从 0 到 100% 的进度条在平滑推进,等待的焦虑感会大幅降低。进度条不仅是一个视觉元素,更是一个心理工具——它告诉用户"系统在工作,结果即将到来"。

HarmonyOS NEXT ArkUI 提供了 Progress 组件——一个支持五种样式和高度定制的进度指示器。本文将深入讲解 Progress 组件的完整 API,并构建一个"下载管理器"——支持多任务并发下载、实时进度模拟、暂停/继续操作和已完成清理。

关键词:HarmonyOS、ArkUI、Progress、进度条、下载管理、实时模拟、setInterval

一、Progress 组件 API

1.1 基本用法

Progress({ value: this.progress, total: 100, type: ProgressType.Linear })
  .width('100%')
  .height(6)
  .color('#1677FF')
  .backgroundColor('#F2F3F5')

核心参数与属性:

参数/属性 类型 说明
value number 当前进度值
total number 总进度值(默认 100)
type ProgressType 进度条样式
.color() ResourceColor 进度填充颜色
.backgroundColor() ResourceColor 轨道背景颜色
.width() Length 进度条宽度
.height() Length 进度条高度(线性时指粗细)

1.2 五种样式

Progress 组件支持五种样式,通过 ProgressType 枚举选择:

样式 ProgressType 说明 适用场景
线性 Linear 水平进度条 文件下载、任务进度
环形 Ring 圆环进度,从上端顺时针填充 加载等待、刷新
刻度环形 ScaleRing 带刻度标记的环形 存储空间、电量
月食 Eclipse 月牙形渐变填充 上传进度、数据同步
胶囊 Capsule 胶囊形状,可附加文本 评分、完成度

本文 Demo 使用 Linear 样式——下载管理器中最直观的选择,每个任务一条独立的水平进度条。

1.3 动态更新 value

Progress 本身是静态的——它只渲染 value 参数的当前值。要让进度"动起来",需要通过定时器不断更新 value

private timerId: number = -1;

startSimulation(): void {
  if (this.timerId !== -1) return;
  this.timerId = setInterval(() => {
    this.tasks = this.tasks.slice().map((task: DownloadTask) => {
      if (task.status === 'downloading' && task.progress < 100) {
        let inc = Math.floor(Math.random() * 6) + 3; // 增量 3~8
        let np = task.progress + inc;
        if (np >= 100) return task.withComplete();
        return task.withProgress(np);
      }
      return task;
    });
  }, 400);
}

每 400 毫秒,所有"下载中"的任务进度增加一个随机值(3~8)。当进度达到或超过 100 时,任务自动标记为完成。随机增量模拟了真实网络下载的速度波动——有些时刻快,有些时刻慢,避免"匀速推进"的机械感。

1.4 定时器的生命周期管理

定时器在 aboutToAppear 中启动,在 aboutToDisappear 中销毁:

aboutToAppear(): void {
  this.startSimulation();
}

aboutToDisappear(): void {
  this.stopSimulation();
}

stopSimulation(): void {
  if (this.timerId !== -1) {
    clearInterval(this.timerId);
    this.timerId = -1;
  }
}

如果不在页面销毁时清除定时器,它会继续在后台运行并尝试更新已销毁组件的状态——这可能导致内存泄漏或运行时错误。timerId 是一个普通类字段(非 @State),因为它只用于定时器控制,不参与 UI 渲染。

二、下载管理器的整体设计

2.1 页面架构

DownloadPage
├── 标题栏 — "下载管理" + 进行中计数
├── 添加下载区域 — 说明文字 + "+ 添加"按钮
├── 下载任务列表(Scroll)
│   ├── 任务 1: 文件名 + 大小 + 状态 + 进度条 + 百分比 + 操作按钮
│   ├── 任务 2: ...
│   └── 任务 3: ...
├── 清除已完成按钮(仅在有已完成任务时显示)
└── 组件说明卡片

2.2 数据结构

class DownloadTask {
  id: number;
  fileName: string;   // 文件名
  fileSize: string;   // 文件大小(文本展示)
  progress: number;   // 下载进度 0-100
  status: string;     // 'downloading' | 'paused' | 'completed' | 'failed'
}

DownloadTaskclass(而非 interface),原因与之前的文章一致——ArkTS 不支持对象展开运算符,需要用工厂方法(withProgresswithStatuswithComplete)创建修改后的新对象:

class DownloadTask {
  withProgress(p: number): DownloadTask {
    return new DownloadTask(this.id, this.fileName, this.fileSize, p, this.status);
  }
  withStatus(s: string): DownloadTask {
    return new DownloadTask(this.id, this.fileName, this.fileSize, this.progress, s);
  }
  withComplete(): DownloadTask {
    return new DownloadTask(this.id, this.fileName, this.fileSize, 100, 'completed');
  }
}

2.3 初始任务

Demo 预置了 3 个下载任务,覆盖三种状态:

任务 文件 大小 初始进度 状态
1 HarmonyOS_SDK_6.1.1.zip 856 MB 42% 下载中
2 DevEco_Studio_Setup.dmg 2.1 GB 0% 已暂停
3 arkui_component_library.har 3.8 MB 100% 已完成

三种状态各有对应的 UI 样式:下载中显示蓝色进度条、已暂停显示灰色进度条、已完成显示绿色进度条。
在这里插入图片描述

三、进度条的美学设计

3.1 颜色与状态映射

if (task.status !== 'failed') {
  Progress({ value: task.progress, total: 100, type: ProgressType.Linear })
    .width('100%')
    .height(6)
    .color(task.status === 'completed' ? '#52C41A' :
      (task.status === 'paused' ? '#CCCCDD' : '#1677FF'))
    .backgroundColor('#F2F3F5')
}

三种颜色的语义:

  • 蓝色 #1677FF:下载中——积极、进行中
  • 灰色 #CCCCDD:已暂停——中性、等待中
  • 绿色 #52C41A:已完成——成功、积极完成

轨道背景统一使用 #F2F3F5(浅灰),与页面背景色一致,确保进度条在任何状态下都有清晰的视觉对比。进度条高度设为 6px——足够高以便看清填充状态,又不会在列表中显得过于粗重。

3.2 百分比显示

进度条右侧显示精确百分比:

Text(task.progress.toString().concat('%'))
  .fontSize(13)
  .fontColor(task.status === 'completed' ? '#52C41A' :
    (task.status === 'paused' ? '#CCCCDD' : '#1677FF'))
  .fontWeight(FontWeight.Medium)
  .width(40)
  .textAlign(TextAlign.End)

百分比文字的颜色与进度条颜色一致,形成"进度条-百分比"的视觉连线。宽度固定为 40px 并右对齐,确保不同进度的数值(“7%”、“42%”、“100%”)在垂直方向上对齐,不会因文字宽度变化而左右晃动。

3.3 状态标签

每个任务卡片的右上角有一个状态标签:

Text(this.getStatusText(task.status))
  .fontSize(11)
  .fontColor(this.getStatusColor(task.status))
  .padding({ top: 3, bottom: 3, left: 8, right: 8 })
  .borderRadius(8)
  .backgroundColor(this.getStatusColor(task.status).concat('18'))

标签使用主题色文字 + 低透明度同色背景(.concat('18') 追加 10% 不透明度的 alpha 通道),形成优雅的彩色标签。四个状态的映射:

在这里插入图片描述

四、下载任务操作

4.1 暂停与继续

每个进行中或已暂停的任务都有一个"暂停"或"继续"按钮:

if (task.status === 'downloading' || task.status === 'paused') {
  Text(task.status === 'downloading' ? '⏸ 暂停' : '▶ 继续')
    .onClick(() => { this.togglePause(task.id); })
}

togglePause 在两个状态之间切换:

togglePause(id: number): void {
  this.tasks = this.tasks.slice().map((task: DownloadTask) => {
    if (task.id === id) {
      if (task.status === 'downloading') return task.withStatus('paused');
      if (task.status === 'paused') return task.withStatus('downloading');
    }
    return task;
  });
  this.startSimulation();
}

切换到"继续"后调用 startSimulation() 确保定时器处于运行状态(因为所有任务可能之前都处于暂停/完成状态,定时器已自动停止)。

4.2 取消与删除

进行中的任务显示红色"取消"按钮,已完成的任务显示灰色"删除"按钮:

if (task.status !== 'completed') {
  Text('取消')
    .fontColor('#FF4D4F')
    .backgroundColor('#FFF1F0')
    .onClick(() => { this.removeTask(task.id); })
}
if (task.status === 'completed') {
  Text('删除')
    .fontColor('#BBBBCC')
    .backgroundColor('#F8F9FA')
    .onClick(() => { this.removeTask(task.id); })
}

"取消"使用红色调(文字 #FF4D4F + 背景 #FFF1F0),暗示这是一个"破坏性"操作。"删除"使用灰色,表明这是一个中性的清理操作。两者调用的 removeTask 是同一个方法——通过 slice().filter() 创建不包含该 ID 的新数组:

removeTask(id: number): void {
  this.tasks = this.tasks.slice().filter((task: DownloadTask) => task.id !== id);
}

4.3 添加下载

用户点击"+ 添加"按钮后,从预设库中按顺序选取文件添加到下载队列:

addDownload(): void {
  let downloads = this.getPresetDownloads();
  let existing = this.tasks.length;
  let idx = existing % downloads.length;
  let d = downloads[idx];
  let task = new DownloadTask(this.nextId, d.name, d.size, 0, 'downloading');
  this.nextId++;
  this.tasks = this.tasks.slice().concat(task);
  this.startSimulation();
}

使用 existing % downloads.length 循环选取预设文件——第 4、9、14… 次添加会回到第一个预设文件。通过 slice().concat(task) 追加新任务(而非 push)以触发 @State 响应式更新。添加后调用 startSimulation() 启动定时器。

预设库包含 5 个不同类型和大小文件:

文件名 大小 模拟场景
harmonyos_docs_2026.pdf 24 MB 小文档
sample_project_template.zip 4.5 MB 小压缩包
media_resources_bundle.tar 320 MB 中型资源包
debug_symbols_package.7z 1.8 GB 大型包
ui_mockup_design.fig 18 MB 设计文件

4.4 清除已完成

当列表中存在已完成任务时,底部显示"清除已完成任务"按钮:

if (this.tasks.filter((task: DownloadTask) =>
  task.status === 'completed').length > 0) {
  Row() {
    Text('清除已完成任务')
  }
  .onClick(() => { this.clearCompleted(); })
}

clearCompleted 过滤掉所有已完成状态的任务:

clearCompleted(): void {
  this.tasks = this.tasks.slice()
    .filter((task: DownloadTask) => task.status !== 'completed');
}

这是一个批量清理操作——在下载管理器中,用户通常会在多个文件下载完成后一次性清理,而不是逐个删除。

五、标题栏的动态计数

标题栏右侧显示进行中的任务数:

Text(this.getActiveCount().toString().concat(' 个进行中'))
getActiveCount(): number {
  return this.tasks.filter((task: DownloadTask) =>
    task.status === 'downloading' || task.status === 'paused').length;
}

当用户添加新下载时,数字增加;当任务完成或取消时,数字减少。这个动态计数让用户无需滚到列表底部就能感知下载队列的状态。

六、定时器的自动停止

定时器在两种情况下自动停止:

  1. 所有任务非下载中:当 startSimulation 的回调中检测不到任何 status === 'downloading' 的任务时,自动 clearInterval
if (!hasActive) {
  this.stopSimulation();
}
  1. 页面销毁时aboutToDisappear 中调用 stopSimulation

这意味着定时器只在"需要时"运行——有下载任务在进行。这种设计避免了不必要的 CPU 开销和电池消耗。当用户暂停最后一个下载任务后,定时器立即停止;当用户恢复下载或添加新任务时,startSimulation() 重新启动定时器。

七、交互流程演示

7.1 初始状态

进入页面,3 个预置任务显示在列表中。任务 1(SDK)的进度条在 42% 处,蓝色进度条以随机速度推进。任务 2(DevEco Studio)显示灰色进度条和"已暂停"状态。任务 3(组件库)显示绿色进度条和"已完成"状态。标题栏显示"2 个进行中"。

7.2 自动完成

约 30-40 秒后,任务 1 的进度条到达 100%,自动变为绿色,状态变为"已完成"。标题栏计数变为"1 个进行中"。

7.3 暂停与继续

点击任务 1 的"⏸ 暂停"按钮——进度条变为灰色,状态变为"已暂停"。标题栏计数变为"0 个进行中",定时器自动停止。

点击"▶ 继续"按钮——进度条恢复蓝色,定时器重新启动。起止在之前暂停的进度值继续推进。

7.4 添加与取消

点击"+ 添加"按钮 3 次,新增 3 个下载任务出现在列表底部,进度从 0% 开始推进。标题栏计数更新为"4 个进行中"。

点击其中某个任务的"取消"按钮——该任务从列表中移除。标题栏计数自动更新。

7.5 清除已完成

点击底部的"清除已完成任务"——所有绿色状态的任务被移除,列表变干净。Toast 提示"已清除已完成任务"。

八、总结

本文通过"下载管理器"这个实战案例,全面讲解了 ArkUI Progress 进度条组件的使用方法。核心知识点包括:

  1. Progress 五种样式:Linear(线形)、Ring(环形)、ScaleRing(刻度环形)、Eclipse(月食)、Capsule(胶囊)
  2. Progress 配置value / total / .color() / .backgroundColor() / .height()
  3. 定时器模拟进度setInterval + 随机增量 + 自动完成检测
  4. 定时器生命周期aboutToAppear 启动 + aboutToDisappear 清理 + 空闲自动停止
  5. 任务状态管理:下载中 / 已暂停 / 已完成 / 失败,颜色语义化映射
  6. 批量操作:添加下载、暂停/继续、取消/删除、清除已完成
  7. ArkTS 不可变更新slice().map() + 工厂方法替代展开运算符

进度条看似简单,但用好它需要理解的不只是 API——更是状态管理、定时器生命周期和视觉一致性的综合能力。一个好的进度指示不仅是"从 0 到 100"的填充动画,更是让用户在等待中保持耐心的心理工具。ArkUI 的 Progress 组件提供了灵活的样式和配置选项,让开发者可以针对不同场景选择最合适的呈现方式。


Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐