HarmonyOS7 加载状态展示页实战:LoadingProgress 不只是会用,还要用得顺手

文章目录

      • 加载不只是转圈
      • `startLoading()` 的关键是收尾
      • 按钮加载要防止重复提交
      • 页面结构怎么看
      • 完整代码
      • 写在最后

加载状态最怕两种情况:一种是用户点了按钮没反应,以为卡死了;另一种是 Loading 一直转,用户不知道还要等多久。LoadingProgress本身不复杂,难的是把它和真实任务状态绑在一起。

这个 HarmonyOS7 页面把基础加载、按钮加载、任务进度和全屏遮罩放到一起看,正好能覆盖日常开发里最常见的几种等待场景。

加载不只是转圈

页面里有isPageLoadingisButtonLoadingloadProgresstasks。这几个状态分别对应页面级等待、按钮提交等待、进度条数值和任务列表。

这样拆的好处是状态含义很清楚。按钮提交时只锁按钮,不一定要遮住整个页面;全屏加载适合页面初始化或关键操作;任务进度则适合文件上传、资源下载这类可量化过程。

startLoading()的关键是收尾

startLoading()先把页面加载状态打开,再用定时器模拟进度增长。真正值得注意的是到达 100 后的处理:清理定时器、关闭加载态、把进度固定到 100。

startLoading(){this.isPageLoading=truethis.loadProgress=0this.timer=setInterval(()=>{if(this.loadProgress>=100){clearInterval(this.timer)this.isPageLoading=falsethis.loadProgress=100}else{this.loadProgress+=5}},100)}

做真实接口时也一样,开始请求前进入 loading,请求结束后一定要退出 loading。失败时也要退出,不然用户会一直等下去。

按钮加载要防止重复提交

startButtonLoading()这种场景很常见:登录、提交订单、保存表单。按钮一旦进入加载态,就应该改变文案或显示加载图标,同时避免用户连续点击。

这里的重点不是动画,而是交互承诺。用户点了按钮,页面必须告诉他“我收到了,正在处理”。

页面结构怎么看

LoadingStatePage分成基础 LoadingProgress、按钮加载、任务进度和全屏遮罩四块。读代码时可以先看状态字段,再看两个启动方法,最后看 UI 根据状态如何切换文案、进度和遮罩。

如果你把这页改成真实业务,我建议把模拟定时器换成接口回调或上传进度回调。加载状态要跟真实任务走,别只做一个假动画。

完整代码

下面是完整代码。入口组件名是LoadingStatePage,可以作为 HarmonyOS7 加载状态设计的参考页面。

// LoadingProgress 加载动画interfaceLoadTask{id:numbername:stringprogress:numberstatus:stringcolor:string}@Entry@Componentstruct LoadingStatePage{@StateisPageLoading:boolean=false@StateisButtonLoading:boolean=false@StateloadProgress:number=0@Statetasks:LoadTask[]=[{id:1,name:'下载核心资源',progress:100,status:'已完成',color:'#059669'},{id:2,name:'加载用户数据',progress:75,status:'加载中',color:'#2563EB'},{id:3,name:'同步配置文件',progress:40,status:'加载中',color:'#D97706'},{id:4,name:'预加载图片缓存',progress:10,status:'等待中',color:'#9CA3AF'},]privatetimer:number=-1startLoading(){this.isPageLoading=truethis.loadProgress=0this.timer=setInterval(()=>{if(this.loadProgress>=100){clearInterval(this.timer)this.isPageLoading=falsethis.loadProgress=100}else{this.loadProgress+=5}},100)}startButtonLoading(){this.isButtonLoading=truesetTimeout(()=>{this.isButtonLoading=false},3000)}build(){Column({space:0}){// 顶部标题Text('LoadingProgress 加载动画').fontSize(20).fontWeight(FontWeight.Bold).fontColor('#111827').width('100%').padding({left:20,right:20,top:20,bottom:4})Text('展示各种加载状态场景下的最佳实践').fontSize(13).fontColor('#6B7280').width('100%').padding({left:20,right:20,bottom:16})// 一、内联加载动画Column({space:12}){Text('一、基础 LoadingProgress').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#374151').width('100%')Row({space:24}){// 默认色Column({space:6}){LoadingProgress().width(40).height(40).color('#2563EB')Text('蓝色').fontSize(12).fontColor('#6B7280')}// 绿色Column({space:6}){LoadingProgress().width(40).height(40).color('#059669')Text('绿色').fontSize(12).fontColor('#6B7280')}// 橙色Column({space:6}){LoadingProgress().width(40).height(40).color('#D97706')Text('橙色').fontSize(12).fontColor('#6B7280')}// 大尺寸Column({space:6}){LoadingProgress().width(60).height(60).color('#7C3AED')Text('大号').fontSize(12).fontColor('#6B7280')}// 小尺寸Column({space:6}){LoadingProgress().width(24).height(24).color('#DC2626')Text('小号').fontSize(12).fontColor('#6B7280')}}.width('100%').justifyContent(FlexAlign.SpaceAround).padding({top:12,bottom:12}).backgroundColor('#F9FAFB').borderRadius(12)}.width('100%').padding({left:20,right:20}).alignItems(HorizontalAlign.Start).margin({bottom:16})// 二、按钮加载状态Column({space:12}){Text('二、按钮加载状态').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#374151').width('100%')Row({space:12}){if(this.isButtonLoading){Button({type:ButtonType.Normal}){Row({space:8}){LoadingProgress().width(18).height(18).color(Color.White)Text('提交中...').fontSize(14).fontColor(Color.White)}}.backgroundColor('#2563EB').borderRadius(8).padding({left:20,right:20,top:10,bottom:10})}else{Button('提交表单').fontSize(14).backgroundColor('#2563EB').fontColor(Color.White).borderRadius(8).padding({left:20,right:20,top:10,bottom:10}).onClick(()=>this.startButtonLoading())}Text(this.isButtonLoading?'3秒后恢复':'点击触发加载').fontSize(12).fontColor('#9CA3AF')}.width('100%').alignItems(VerticalAlign.Center)}.width('100%').padding({left:20,right:20}).alignItems(HorizontalAlign.Start).margin({bottom:16})// 三、任务进度列表Column({space:12}){Text('三、任务进度展示').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#374151').width('100%')Column({space:8}){ForEach(this.tasks,(task:LoadTask)=>{Row(){if(task.progress<100){LoadingProgress().width(20).height(20).color(task.progress===10?'#9CA3AF':task.color).margin({right:10})}else{Text('✅').fontSize(18).width(20).height(20).margin({right:10})}Column({space:4}){Row(){Text(task.name).fontSize(13).fontColor('#1F2937').layoutWeight(1)Text(task.status).fontSize(11).fontColor(task.progress===100?'#059669':task.color)}Progress({value:task.progress,total:100,type:ProgressType.Linear}).color(task.color).backgroundColor('#E5E7EB').style({strokeWidth:4})}.layoutWeight(1)}.width('100%').padding({left:12,right:12,top:10,bottom:10}).backgroundColor('#FAFAFA').borderRadius(8).alignItems(VerticalAlign.Center)},(task:LoadTask)=>task.id.toString())}}.width('100%').padding({left:20,right:20}).alignItems(HorizontalAlign.Start).margin({bottom:16})// 四、全屏蒙层加载Column({space:12}){Text('四、全屏遮罩加载').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#374151').width('100%')Row({space:12}){Button('触发全屏加载').fontSize(13).backgroundColor('#7C3AED').fontColor(Color.White).borderRadius(8).padding({left:16,right:16,top:10,bottom:10}).onClick(()=>this.startLoading())if(this.loadProgress>0){Text(`${this.loadProgress}%`).fontSize(16).fontWeight(FontWeight.Bold).fontColor('#7C3AED')}}.width('100%').alignItems(VerticalAlign.Center)}.width('100%').padding({left:20,right:20}).alignItems(HorizontalAlign.Start)Blank()// 全屏遮罩if(this.isPageLoading){Column({space:16}){LoadingProgress().width(60).height(60).color(Color.White)Text(`加载中${this.loadProgress}%`).fontSize(16).fontColor(Color.White)Progress({value:this.loadProgress,total:100,type:ProgressType.Linear}).width(160).color(Color.White).backgroundColor('rgba(255,255,255,0.3)').style({strokeWidth:4})}.width('100%').height('100%').backgroundColor('rgba(0,0,0,0.65)').justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)}}.width('100%').height('100%').backgroundColor('#FFFFFF')}}

写在最后

加载状态展示页 这个案例的价值,不在于用了多少组件,而在于它把LoadingProgress放进了一个完整页面里。照着这个思路改自己的业务,比单独背 API 更靠谱。