恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

HarmonyOS ArkTS加载指示器实战:从基础到多选删除场景

  • 首页
  • 资讯中心
  • /
  • HarmonyOS ArkTS加载指示器实战:从基础到多选删除场景

相关资讯

YOLO工程落地实战:v8/v11/v12统一框架与SpringBoot高并发部署 2026/9/11 9:47:43
工业级160128液晶模块驱动实战:时序控制与抗干扰设计 2026/9/11 9:47:43
手机钱包.zip工程:解压、构建到上架全流程详解 2026/9/11 9:42:43

最新资讯

RC一阶电路暂态响应实验:从示波器测量到误差分析的全流程复盘
亚马逊卖家服务商市场平台有哪些?AI Agent融入业务后从传统导航到AI驱动的进化路径
PyQt6自定义窗口标题栏开发指南
电压型VSG离网仿真建模与MATLAB实现指南
C++继承进阶:避开六大坑,掌握设计原则与组合思维
基于 Azure OpenAI 构建图像生成应用:从 DALL-E 原理到 gpt-image-1 与元提示实战

今日推荐

YOLO烟盒数据集目标检测训练全流程:标注校验、格式转换与模型复现
HuffPost新闻数据集解析:JSONL加载与时间感知分类实战
Budibase 本地开发环境搭建与运行指南:从全新克隆到 dev 栈启动的完整实践

本周热门

超人会飞不算本事:系统稳定依赖清晰规则与边界设计
超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论
基于CNN的调制信号识别:MATLAB实现时频图分类实战

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

HarmonyOS ArkTS加载指示器实战:从基础到多选删除场景

发布时间:2026/9/11 9:47:43
HarmonyOS ArkTS加载指示器实战:从基础到多选删除场景 做鸿蒙应用开发这段时间我花在加载指示器Indicator上的时间比想象中多。HarmonyOS 6 下的 ArkTS 声明式 UI 把加载状态组件推到了交互设计的前台——列表加载要转圈、删除操作要提示、刷新按钮要反馈任何一个没有状态反馈的点击都会让用户觉得“卡了”。这篇内容就围绕 Indicator 展开先聊基础用法再讲一个真实的多选列表删除场景最后把我踩过的坑和调试方法一并交代清楚。适合刚接触 ArkTS或者已经在做鸿蒙应用想优化交互细节的同学参考。1. 整体设计与思路拆解1.1 为什么 Indicator 在 ArkTS 里不只是一个“小部件”很多刚上手 ArkTS 的朋友会把 Indicator 理解成“一个会转的小圈圈”真正写业务的时候才发现完全不是这么回事。在鸿蒙的声明式 UI 体系里Indicator 并不是简单拿来就用的静态控件它背后牵涉的是页面状态、异步任务、用户操作反馈这三者的协同。我见过不少项目在列表页把 LoadingProgress 组件直接写在 build 里结果永远都在转也见过有人在删除按钮的点击回调里写死了一行this.isLoading true但忘了在删除完成之后把状态复位导致整个界面卡死在加载遮罩后面。这些问题看起来是“忘了写一行代码”本质上是没有把 Indicator 当成“状态的可视化映射”来设计。在 ArkTS 里界面会随着 State 变量的变化自动刷新。所以正确的姿势是先定义State isLoading: boolean false然后用if (this.isLoading)控制 Indicator 的出现和消失。这样你不是在“控制组件”而是在“控制状态”。状态到位了UI 自然正确。1.2 为什么我选择用 ArkTS 的声明式方式处理 IndicatorHarmonyOS 6 的 ArkUI 框架下我优先用 ArkTS 实现 Indicator而不是去依赖自定义绘制的旧方案核心原因是代码流和状态流能保持同一条线。以前用命令式 UI 的时候加载状态需要手动 add/remove 组件还要担心组件层级、内存释放、重复添加等问题。ArkTS 里由于数据驱动 UIIndicator 的显示和隐藏可以完全跟随业务变量。比如进入页面拉数据时isLoadingtrue数据回来后isLoadingfalse页面上的 LoadingProgress 就自动跟着切换不用再手动去查节点、删节点、控制透明度。另外 ArkTS 的组件复用和状态管理机制也比较适合做复杂交互。比如多选列表删除这个场景选中数量变化、删除按钮的可用性、遮罩层的显示这些都是状态之间的关系。用声明式描述这些关系逻辑会清楚很多。你只需要关系状态本身剩下的交给框架去刷新。提示Indicator 不是只能转圈它本质上是一个“异步反馈通道”。任何耗时超过 200ms 的操作都应该让用户知道当前发生了什么。这是交互设计的基础也是写代码时必须有的意识。2. 基础用法与关键属性实战2.1 系统自带 LoadingProgress上手最快的 IndicatorArkUI 里最直接的 Indicator 实现就是系统自带的 LoadingProgress 组件。它会显示一个循环旋转的加载动画用来表示任务进行中。实际项目里我一般会优先用它因为不需要任何额外资源性能也稳定。最基础的使用方式如下Entry Component struct BasicIndicatorPage { State isLoading: boolean true build() { Column({ space: 16 }) { Text(数据加载中) .fontSize(16) .fontColor(#333333) if (this.isLoading) { LoadingProgress() .width(48) .height(48) .color(#1E88E5) .enableLoading(true) } } .width(100%) .height(100%) .justifyContent(FlexAlign.Center) } }这里有几个属性值得单独说一下。color控制加载指示器的颜色别只会用默认的灰色视觉上要和页面主色一致才有整体感。width和height控制大小我建议至少 36vp太小在手机上看着费力。enableLoading参数是个容易被忽略的点它的作用是控制是否执行加载动画如果你只想提前渲染静态样式可以先用false停住动画。如果你需要更精确的进度反馈比如上传文件的具体百分比那不能只用循环动画。LoadingProgress 支持value参数取值范围 0 到 100。当你传入具体数值时它更像一个进度条能直观反映出任务完成度。注意一点如果是持续性刷新不要每次都重新设置整个组件只要通过数据绑定更新value即可这样动画不会中断。2.2 自定义 Indicator不满足于系统组件时怎么办系统 LoadingProgress 的样式虽然简洁但总有人觉得它“太普通”。我在实际项目里被产品经理提过好几次能不能在加载转圈旁边加文字能不能用品牌色做背景加载结束后能不能带勾选动画这时候就需要自定义 Indicator。在 ArkTS 里实现自定义加载指示器我最常用的是 Stack 加基础组件的组合。一个典型方案是自定义一个带文字的加载遮罩层Component struct CustomIndicator { Prop label: string 加载中... Prop maskOpacity: number 0.3 State rotateAngle: number 0 aboutToAppear() { this.startRotate() } startRotate() { // 通过循环动画驱动旋转角度 animateTo({ duration: 800, iterations: -1, curve: Curve.Linear }, () { this.rotateAngle 360 }) } build() { Stack() { // 半透明遮罩 Column() .width(100%) .height(100%) .backgroundColor(rgba(0, 0, 0, ${this.maskOpacity})) .position({ x: 0, y: 0 }) // 加载卡片 Row({ space: 12 }) { Row() .width(6) .height(6) .margin(8) .borderRadius(3) .backgroundColor(#FFFFFF) .rotate({ angle: this.rotateAngle }) .animation({ duration: 800, iterations: -1, curve: Curve.Linear }) Text(this.label) .fontSize(14) .fontColor(#FFFFFF) } .padding({ left: 20, right: 20, top: 14, bottom: 14 }) .backgroundColor(#CC1E232B) .borderRadius(12) } .width(100%) .height(100%) } }这段代码里animateTo和.rotate配合实现旋转动画。我做了个小细节用一个小圆点代替大转圈配合白色文字在深色遮罩上看起来比较精致。Stack负责让遮罩层铺满整个页面这样不管下面的 List 有多长都能被稳稳盖住。自定义 Indicator 的好处是你能完全控制样式和动画节奏但它也要接受状态管理。Prop传入的文字、遮罩透明度都是可以由父组件动态修改的这就比系统组件灵活很多。注意自定义 Indicator 里的动画在页面销毁时一定要能停掉。如果aboutToDisappear里不做清理长时间运行页面跳转后动画可能还在后台跑白白浪费线程资源。2.3 Indicator 的显示与隐藏用状态控制而不是节点控制很多新人容易搞混一点不管用系统 LoadingProgress 还是自定义 Indicator都不应该在.visibility()属性里写死常驻逻辑。你在 build 里写了组件它就必然存在只是显不显示的问题。更好的做法是直接用if条件渲染。举个例子在列表页加载数据build() { Stack() { if (this.itemList.length 0 this.isLoading) { // 首次加载显示居中 Indicator Column() { LoadingProgress() .width(48) .height(48) .color(#1E88E5) Text(正在加载数据...) .fontSize(14) .fontColor(#999999) .margin({ top: 12 }) } } if (this.itemList.length 0) { List({ space: 12 }) { ForEach(this.itemList, (item: ItemModel) { ListItem() { Text(item.title) .fontSize(16) .padding(16) .backgroundColor(Color.White) .borderRadius(12) } }, (item: ItemModel) item.id.toString()) } .padding(16) } if (this.isLoading this.itemList.length 0) { // 下拉刷新时顶部或中间的遮罩 Indicator CustomIndicator({ label: 刷新中... }) } } .width(100%) .height(100%) }这种写法读起来很直观第一个条件覆盖“首屏加载”第二个条件覆盖“已有内容”第三个条件覆盖“刷新中”。每一个状态分支都是打印在代码里的一段“可视菜单”排查问题的时候扫一眼就知道哪个状态把页面卡住了。同时要提醒一个实践上的细节如果条件渲染的组件结构复杂比如 List 套多个子组件频繁切换时会有性能开销。所以对于 Indicator 这类轻量组件直接用if没问题如果是高频切换的复杂区域再考虑用Visibility属性控制。不要看到哪都用if也不要不分场景都用Visibility根据具体场景做选择。3. 基于 Indicator 的多选列表删除实操3.1 场景描述与页面结构设计热搜里提到了“多选列表删除”这实际上是我在做文件管理类应用时非常常见的一个需求。用户长按某个条目进入多选模式勾选若干项点底部删除按钮系统弹出确认框确认后显示加载指示器删除完成再刷新列表、退出多选模式。这个场景里值得留意的是删除操作可能涉及大量数据也可能要调用后端接口所以不能“点击即删除、瞬间完成”那么乐观。一定需要一个反馈中间态。这时候 Indicator 就派上用场了。页面结构我习惯这样设计build() { Column() { // 顶部标题栏 Row() { Text(this.selectMode ? 已选择 ${this.selectedCount} 项 : 文件列表) .fontSize(18) .fontWeight(FontWeight.Bold) Blank() if (this.selectMode) { Text(全选) .fontSize(14) .fontColor(#1E88E5) .onClick(() this.selectAll()) Text(退出) .fontSize(14) .fontColor(#999999) .margin({ left: 16 }) .onClick(() this.exitSelectMode()) } } .width(100%) .padding({ left: 16, right: 16, top: 12, bottom: 12 }) // 列表区域 Stack() { List({ space: 8 }) { ForEach(this.itemList, (item: ItemModel) { ListItem() { this.buildItemRow(item) } }, (item: ItemModel) item.id.toString()) } .width(100%) .layoutWeight(1) .padding(12) // 正在删除的 Indicator if (this.isDeleting) { CustomIndicator({ label: 正在删除..., maskOpacity: 0.4 }) } } .width(100%) .layoutWeight(1) // 底部操作栏 if (this.selectMode) { Row() { Button(删除选中项) .width(90%) .backgroundColor(#E84026) .enabled(this.selectedCount 0) .onClick(() this.confirmDelete()) } .width(100%) .padding({ top: 12, bottom: 24 }) .backgroundColor(Color.White) } } .width(100%) .height(100%) .backgroundColor(#F2F4F7) }这样整体的层次是顶部标题栏提供状态切换列表区域支持选择底部操作栏收集行为Indicator 遮罩负责异步反馈。各模块职责明确后面接需求改东西也好改。3.2 多选与删除逻辑实现多选模式用两个变量控制selectMode表示当前是否处于多选状态selectedCount表示选中数量。列表数据用State items: ItemModel[]保存。class ItemModel { id: number title: string isSelected: boolean constructor(id: number, title: string, isSelected: boolean false) { this.id id this.title title this.isSelected isSelected } }列表项的点击逻辑分两种状态非多选模式点击直接进入多选模式并默认选中当前项多选模式下点击切换该项的isSelected。onItemClick(item: ItemModel) { if (!this.selectMode) { this.selectMode true item.isSelected true } else { item.isSelected !item.isSelected } this.refreshSelectedCount() } refreshSelectedCount() { this.selectedCount this.itemList.filter((item: ItemModel) item.isSelected).length }注意filter在 ArkTS 里是可以用的但要注意类型标注否则编译器会报类型推导错误。我们这用的是(item: ItemModel) item.isSelected这种写法编译器就能明确知道过滤的是 ItemModel 数组。删除逻辑第一步是把选中的 id 收集起来第二步调用删除方法。删除方法里为了让效果更真实我模拟了一个延迟操作实际项目里会替换成远程接口调用。async confirmDelete() { // 收集选中的 id const deleteIds: number[] [] this.itemList.forEach((item: ItemModel) { if (item.isSelected) { deleteIds.push(item.id) } }) if (deleteIds.length 0) { return } // 进入删除中状态 this.isDeleting true try { // 模拟网络或数据库删除耗时 await this.performDelete(deleteIds) // 移除已删除项 this.itemList this.itemList.filter((item: ItemModel) !deleteIds.includes(item.id)) // 重置选择状态 this.selectMode false this.selectedCount 0 } catch (error) { console.error(delete failed, error: ${JSON.stringify(error)}) } finally { this.isDeleting false } }finally里把isDeleting置回 false这个很关键无论删除成功还是失败加载指示器都必须消失否则用户会被永久卡在遮罩下面。3.3 Indicator 接入的细节和小技巧这个场景里Indicator 不是简单的居中提示而是要作为 Stack 中最高层的子组件覆盖到整个列表上方。我的做法是把 List 和 CustomIndicator 一起包在 Stack 里并且给 CustomIndicator 设置全屏尺寸和半透明遮罩。关键在于 CustomIndicator 的层级默认排在 Stack 后面元素会覆盖在前面的元素上所以你把 CustomIndicator 放在 List 后面它就会盖住 List。如果没有遮罩只显示一个小转圈在底部按钮附近用户可能注意不到操作正在进行。有遮罩可以避免用户继续点击列表项防止在删除过程中误触其他操作体验上更安全。还有一个技巧是点击删除后最好禁用系统返回手势或者顶部返回按钮避免用户在删除过程中退出页面导致状态错乱。这个可以在onBackPress里拦截一下onBackPress(): boolean { if (this.isDeleting) { // 删除进行中不允许返回 return true } return false }这类细节平时不做不会出大错但一旦做了用户对应用的可靠性评价会高很多。我之前在测试环境就遇到过连续快速点击返回导致页面退出去结果删除请求返回后又把列表数据刷了回来界面和逻辑状态对不上。3.4 完整流程跑通后的效果把上面几块拼起来整个流程是这样的用户点击某个列表项selectMode变为 true进入多选状态。用户在列表里继续点击勾选多个项目底部按钮显示“删除选中项”数量和状态实时更新。点击删除按钮isDeleting置为 trueStack 上立刻出现半透明遮罩和“正在删除...”提示。等待删除期间所有点击都被遮罩挡住用户无法修改列表状态。删除完成列表数据过滤掉已删除的项selectMode 退出按钮消失isDeleting置为 falseIndicator 自动关闭。这个过程中 Indicator 一共参与了两次状态切换开场遮罩和结束关闭。整个交互链路顺下来用户不会感觉到“卡顿”或“无响应”因为每一步都有明确的界面反馈。4. 常见问题与排查技巧实录4.1 输出调试用日志定位 Indicator 状态问题网上搜到“ArkTS 输出调试”这个词说明很多新手在状态排查上吃了亏。确实Indicator 这类组件的 bug 很容易出现在状态不对上页面一直转圈、转圈不消失、删除完成后还蒙着一层灰。要快速定位最直接的办法是在关键路径上埋日志。我在项目里一般会用以下几种日志方式// 普通调试信息 console.info(isDeleting: ${this.isDeleting}) console.info(selectMode: ${this.selectMode}, selectedCount: ${this.selectedCount}) // 警告信息 console.warn(isDeleting is still true, check finally block!) // 错误信息 console.error(delete request failed: ${JSON.stringify(error)})DevEco Studio 的 Console 面板会实时输出这些日志你可以用关键字过滤比如输入isDeleting就能只看到状态变化日志。我自己排查 Indicator 问题时最常用的一种方式是专门为核心状态变化写一行日志setDeletingState(value: boolean) { console.info(setDeletingState - ${value}) this.isDeleting value }这样所有对isDeleting的修改都会被记录一旦发现状态没有按预期变化翻日志就能看到是在哪一步漏了赋值。4.2 常见问题速查表现象原因解决办法点击删除后Indicator 没有出现isDeleting没有真正改成 true或者被后面的逻辑立即改回 false在confirmDelete开头打印日志确认状态变化检查finally是否被意外触发Indicator 一直转圈不消失finally中忘记置isDeletingfalse或者异步操作永远在 pending检查异步函数是否正常 resolve/reject在 finally 里做兜底复位删除完成后列表没有刷新删除逻辑操作的是临时数组没有重新赋值给this.itemList用this.itemList this.itemList.filter(...)的方式触发重新渲染列表项复用时选中状态错乱ForEach 的 key 不够稳定比如用 index 当 key改成用 item.id 作为 key保证每个 item 有唯一标识Indicator 盖不住整页列表Indicator 和 List 不在同一个 Stack或者位置没有铺满用 Stack 包住整体给 Indicator 设置 width/height 100%动画卡顿、掉帧自定义 Indicator 中动画驱动了不相关的刷新或者动画循环没有停止动画状态独立成一个变量页面销毁时终止动画循环删除途中快速点击返回页面状态错乱返回事件没有拦截页面销毁后异步请求又改状态在onBackPress里拦截isDeleting状态删除完成前禁止返回自定义 Indicator 文字不更新修改的是普通变量不是 Prop/State 变量把传入文字改成 Prop父组件通过状态变更刷新这张表基本覆盖了我做列表删除场景时遇到的大部分问题。你会发现很多问题不是 Indicator 本身不会写而是状态没有闭环。加载状态、选中状态、数据列表状态这三个只要有一个不是通过 State 统一管理就容易出问题。4.3 关于多选删除与 Indicator 的几个操作心得第一个心得Indicator 的遮罩不要全黑透明度控制在 0.3 到 0.5 之间。全黑遮罩在视觉上太压迫用户会以为应用崩了太透明则盖不住干扰信息。我给的参数默认是 0.3在深色背景页面可以适当调高一点。第二个心得删除成功后可以考虑给用户一个轻提示比如promptAction.showToast显示“已删除 3 项”。因为加载指示器消失的瞬间列表会刷新用户可能还没反应过来发生了什么一个 toast 能补全反馈闭环。第三个心得不要把 Indicator 的显示隐藏逻辑散落在多个生命周期方法里。我见过有人在这个方法里写this.isDeleting true又在另一个回调里写this.isDeleting true最后状态混乱。统一收敛到一个入口函数里比如beginDelete()和finishDelete()所有逻辑集中管理可读性和维护性都会好很多。第四个心得ArkTS 的强类型检查比较严格写filter、forEach、includes这些方法时一定要显式标注参数类型。我在第一次写this.itemList.filter(item ...)时编译器直接报类型错误当时还觉得奇怪。后来习惯每个回调都写上(item: ItemModel) 问题就没了。这个习惯在 ArkTS 里很实用能避免很多隐性 bug。5. 一点个人体会从系统 LoadingProgress 到自定义 Indicator再到把它接入多选列表删除这样完整的业务场景我最大的体会是Indicator 不是 UI 层面的“锦上添花”而是状态管理是否扎实的试金石。一个需要加载反馈的页面如果你能用状态变量清晰控制它那说明你对页面逻辑的拆解是清晰的如果你写起来手忙脚乱大概率是业务状态本身设计得不够干净。我在实际项目中后期已经很少直接写this.isLoading true; this.isLoading false这种零散代码了。更稳定的做法是把加载状态封装成一个独立的PageState枚举或类比如Loading、Success、Error、Empty然后通过switch去渲染对应的 UI。Indicator 只是这个状态机里的一个小分支但它和列表、异常页、空态协同工作时整个应用体验才会真正完整。最后再给一个建议如果你准备在 HarmonyOS 6 上做复杂列表交互动手前先把状态理清。Indicator 用起来不难难的是保证它在任何异步结果下都能正确关闭。多写几个辅助函数、多打几行调试日志都比事后在用户反馈里找 bug 要省力得多。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号