文件下载、任务执行、数据加载——几乎所有需要等待的场景都需要进度指示。用户盯着一个静态的"加载中"文字时,每一秒都漫长;但如果看到一个从 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')

核心参数与属性:

参数/属性类型说明
valuenumber当前进度值
totalnumber总进度值(默认 100)
typeProgressType进度条样式
.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'
}

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

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 个下载任务,覆盖三种状态:

任务文件大小初始进度状态
1HarmonyOS_SDK_6.1.1.zip856 MB42%下载中
2DevEco_Studio_Setup.dmg2.1 GB0%已暂停
3arkui_component_library.har3.8 MB100%已完成

三种状态各有对应的 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.pdf24 MB小文档
sample_project_template.zip4.5 MB小压缩包
media_resources_bundle.tar320 MB中型资源包
debug_symbols_package.7z1.8 GB大型包
ui_mockup_design.fig18 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

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐