当前位置: 首页 > news >正文

第91篇 | HarmonyOS 空态与加载态:相册、视频、保险箱都不能空白

第91篇 | HarmonyOS 空态与加载态:相册、视频、保险箱都不能空白

一个成熟页面不能只设计“有数据”的样子。相册第一次打开、系统相册导入前、视频还没生成、保险箱还没有私密记录,这些状态如果只是白屏,用户会以为应用坏了。双镜记忆相机把加载态、空态和行动按钮都写进页面,帮助用户知道下一步该做什么。

这一篇从galleryLoading开始,串起相册、视频管理和保险箱三个页面。重点不在“写一句没有数据”,而在空态是否有下一步入口,加载态是否防止重复操作,失败态是否回到可理解文案。

本篇目标

  • 区分 loading、empty、error 三类状态,避免都用空白页代替。
  • 看懂loadGalleryRecords如何保证加载开始和结束都能更新状态。
  • 检查相册、视频、保险箱三处空态是否有行动入口。
  • 把空态作为发布前必测项,而不是最后补文案。

对应源码位置

  • superImage/entry/src/main/ets/pages/Index.ets
  • superImage/entry/src/main/ets/services/GalleryRecordService.ets

加载态必须能结束

loadGalleryRecords里先判断是否正在加载,避免重复进入;随后把galleryLoading置为 true,读取完成或失败后都进入 finally 分支改回 false。这个结构比单纯在成功分支关 loading 更稳。

如果 finally 漏掉,用户遇到解析失败或存储异常时,页面可能一直显示“正在整理照片”。这类问题在真机上很难靠肉眼复现,所以需要从源码结构检查。

加载态和空态一起设计,页面才不会在无数据时变成白屏

private async loadGalleryRecords(): Promise<void> { if (this.galleryLoading) { this.galleryLoadQueued = true; return; } this.galleryLoading = true; try { do { this.galleryLoadQueued = false; const records = await GalleryRecordService.loadRecords(this.getAbilityContext()); await this.applyGalleryRecords(records); } while (this.galleryLoadQueued); } catch (error) { const err = error as BusinessError; this.galleryNoticeText = `读取相册失败 ${err.code ?? -1}`; } finally { this.galleryLoading = false; }

相册空态要给出拍摄入口

相册页如果没有记录,页面不应该结束在“暂无照片”。项目里的空态会说明拍照完成后照片会进入这里,并提供“去相机拍摄”的入口。这样用户第一次安装后也能顺着路径走下去。

空态文案要和产品能力一致:这里不是系统相册浏览器,而是双镜记忆记录,所以空态要引导用户回到相机页生成第一条记忆。

相册页在 loading、分组列表、普通列表和空态之间明确分支

void this.importSystemAlbumPhotos('gallery'); }) } Scroll() { Column({ space: 14 }) { if (this.galleryLoading) { Text('正在加载...') .fontSize(13) .lineHeight(20) .fontColor($r('app.color.album_on_surface')) } if (this.getFeaturedGalleryRecord()) { this.buildGalleryMovieEntryCard() if (this.getGalleryGroups().length > 0) { Column({ space: 10 }) { Row() { Text('按时间地点') .fontSize(13) .fontColor($r('app.color.album_accent')) Blank() Text(`${this.getGalleryGroups().length}组`) .fontSize(11) .fontColor($r('app.color.album_on_surface_variant')) } .width('100%') ForEach(this.getGalleryGroups(), (group: GalleryDatePlaceGroup) => { this.buildGalleryAlbumGroupSection(group) }, (group: GalleryDatePlaceGroup) => group.key) } .width('100%') } else if (this.getGalleryListRecords().length > 0) { Column({ space: 10 }) { Text('全部照片') .fontSize(13) .fontColor($r('app.color.album_accent')) ForEach(this.getGalleryListRecords(), (record: GalleryMoment) => { this.buildGalleryRecordCard(record) }, (record: GalleryMoment) => record.id) } .width('100%') } } else {

视频页空态要回到选照片

视频管理页的空态不能引导拍照就结束,因为用户可能已经有照片,只是还没有生成视频。这里更合适的下一步是“去选照片”,让用户从已有记录进入成片流程。

这就是空态设计的细节:同样是没有数据,相册页和视频页的下一步不同,不能复用一句统一文案。

视频页空态把下一步指向选照片,而不是简单提示为空

private buildGalleryVideoManagerPage() { Column({ space: 16 }) { Column({ space: 6 }) { Text('短片') .fontSize(30) .fontWeight(FontWeight.Bold) .fontColor($r('app.color.album_on_surface')) .textAlign(TextAlign.Center) Text(this.getVideoManagerRecordsForRender().length > 0 ? `${this.getVideoManagerRecordsForRender().length}\u6761` : '') .fontSize(13) .lineHeight(20) .fontColor($r('app.color.album_on_surface_variant')) } .width('100%') .alignItems(HorizontalAlign.Center) this.buildGalleryMediaSwitch() this.buildGalleryCloudSyncCard() Scroll() { Column({ space: 14 }) { this.buildGalleryMovieEntryCard() if (this.getVideoManagerRecordsForRender().length === 0) { Column({ space: 12 }) { Text('\u8fd8\u6ca1\u6709\u89c6\u9891') .fontSize(18) .fontWeight(FontWeight.Medium) .fontColor($r('app.color.album_on_surface')) Text('\u5148\u53bb\u7167\u7247\u91cc\u9009\u56fe') .fontSize(13) .lineHeight(20) .fontColor($r('app.color.album_on_surface_variant')) Button('\u53bb\u9009\u62e9') .height(42) .width('100%') .fontSize(14) .fontWeight(FontWeight.Medium) .fontColor($r('app.color.album_on_primary')) .backgroundColor($r('app.color.album_primary_container')) .borderRadius(18) .onClick(() => { this.switchGalleryMediaTab('photo'); }) } .width('100%') .padding(18) .backgroundColor($r('app.color.album_panel')) .borderRadius(24) .alignItems(HorizontalAlign.Start) } else { ForEach(this.getVideoManagerRecordsForRender(), (record: GalleryVideoRecord) => { this.buildVideoManagerRecordCardV2(record) }, (record: GalleryVideoRecord) => record.id) } } .width('100%') } .layoutWeight(1) .scrollBar(BarState.Off) this.buildBottomNavigation()

保险箱空态要尊重解锁状态

保险箱页还多一层隐私状态:没有私密记录、未解锁、有记录且已解锁,这三种状态不能混成一种。项目会先显示云同步卡片,再根据vaultUnlocked和记录数量决定导入、解锁或展示记录。

验收时不要只看默认未解锁状态。至少要测:没有私密记录、导入一张私密照片后未解锁、解锁后查看详情、重新上锁后状态恢复。

保险箱空态必须同时考虑隐私状态和记录数量

空态的标准不是“没有报错”,而是用户能理解当前为什么没有内容,并能找到下一步动作。

工程验收表

检查项通过标准
加载态读取成功、失败、空数据都会关闭 loading。
相册空态首次进入能看到去相机拍摄的明确入口。
视频空态没有成片时能进入选照片流程。
保险箱空态未解锁、无私密记录、有私密记录三种状态不混淆。

真机复测口令

先清空相册记录,再关闭网络,随后分别进入相册页、视频管理页和保险箱页。预期结果是页面有明确空态或失败文案,按钮仍然给出下一步入口,loading 不会一直停留在屏幕上。

再做一次反向测试:导入一条记录后立即离开页面再返回,观察galleryLoading是否能正确结束,列表是否重新刷新。空态文章最怕只写“没有数据”,真正要验的是“没有数据时用户还能做什么”。

今日练习

  1. 手动制造一次相册读取失败,确认 finally 分支会关闭 loading。
  2. 清空视频任务列表,检查视频管理页是否有下一步提示。
  3. 在保险箱未解锁时进入页面,确认私密内容不会提前展示。
http://www.jsqmd.com/news/1006255/

相关文章:

  • 北京朝阳区黄金回收去哪里好?这些情况要先搞清楚 - 新闻快传
  • 手写一个mini版Spring:自动注册 + 依赖注入
  • 高效处理海量多组学数据的统一分析框架实战指南
  • AI智能体:企业数字化转型的加速器
  • 广州首饰回收靠谱指南|避坑 + 报价 + 流程全解析 - 讯息早知道
  • 2026深圳江诗丹顿回收避坑攻略!新手变现不亏实操干货 - 薛定谔的梨花猫
  • 2026昆明装修公司白皮书:5大本土实力装企实力解析 - 装修新知
  • 2026 年 GEO 公司选型手册:合规 技术 实战多维测评下的五强优选厂商 - 互联网科技品牌测评
  • 别再为论文配图发愁了!手把手教你用Ovito渲染LAMMPS轨迹文件(附气泡成核、结冰等案例)
  • Finsler几何在相对论超曲面理论中的应用
  • 10分钟解锁微信语音:silk-v3-decoder如何让特殊音频格式重获新生
  • 2026 武汉计算机408线下培训机构深度测评:这家本土小班是真靠谱 - 小途xt
  • 甲基硫菌灵农药残留检测卡快速检测果蔬中的甲基硫菌灵农药残留
  • Linux学习环境搭建
  • 第93篇 | HarmonyOS 生命周期刷新:返回页面后数据为什么要重新读
  • 2026视频号视频怎么保存到相册?视频号视频保存到相册方法全攻略
  • 第1节:初识C语言
  • 个人档案是什么终于搞懂了,毕业再也不怕处理档案了! - 慧办好
  • 装修不踩雷!汉中装修设计品牌挑选思路与经验分享 - 国麟测评
  • 个人档案查询网上查询如何办理?河南线上查档保姆级教程! - 慧办好
  • 【深度解析】轩麟电永磁吸盘:核心原理与工业应用 - 速递信息
  • 三步告别游戏黑屏:Borderless Gaming让你的游戏窗口无缝切换
  • Windows上运行安卓应用的终极方案:APK安装器完全指南
  • Java MD5加密与Swagger实战教程
  • 北京大兴区黄金回收店评测:三条核心指标筛选,爱回收12家门店全地址 - 新闻快传
  • 嵌入式SRAM深度解析:MC68377操作模式、内存映射与工程实践
  • 北京朝阳区黄金回收店推荐:爱回收24家门店全地址,选店三条标准说清楚 - 新闻快传
  • 终极指南:掌握AlienFX Tools,释放Alienware灯光与风扇的全部潜能
  • 昆山汽车座垫脚垫定制怎么选?车饰源(车舒源)品质突围 - 百航
  • 2026年国内GEO服务商怎么选?这份指南帮你避开80%的踩坑风险 - 速递信息