当前位置: 首页 > 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.cnnetsun.cn/news/2906519.html

相关文章:

  • 二十八.签名与脚本(3)--脚本解析
  • 使用llamafactory进行模型微调完整过程
  • 学习 LPRNet 框架——轻量级车牌识别网络从结构到工程落地
  • Obsidian Copilot终极指南:5分钟打造你的智能第二大脑
  • Cursor Pro破解工具2025完整指南:永久免费使用AI编程助手
  • 桶装水门店客户分层运营:留住老客比拓展新客更重要
  • MC68377嵌入式调试与定时器硬核协同:FASRAM与TPU3实战解析
  • Cursor Pro破解工具2025:如何绕过AI编程助手试用限制的完整技术指南
  • 灯哥开源FOC双路迷你无刷电机驱动实战指南:从入门到精通
  • MonaServer:轻量级多协议服务器框架的终极指南
  • 3个步骤在Windows电脑上安装安卓应用:告别模拟器卡顿的轻量级解决方案
  • 百度网盘Mac版终极提速指南:免费解锁SVIP高速下载功能
  • 《对马岛之魂:导演剪辑版》
  • 5步搭建你的专属游戏云主机:Sunshine游戏串流实战指南
  • XCOM 2模组管理终极指南:告别官方启动器的5大理由
  • 别再死记硬背API了!用请假审批实战,带你玩转Activiti 7的RuntimeService
  • 3分钟学会Blender建筑建模:Building Tools终极指南
  • 2026百色市权威认证贵金属回收 TOP5+黄金回收白银回收铂金回收门店地址电话推荐
  • 学术不端检测技术与科研诚信体系建设实践
  • 别再只看CVSS分数了!手把手教你解读CVE漏洞报告里的那些‘潜台词’
  • Simulink仿真避坑指南:调试BASK/BFSK/BPSK系统时,你的带通滤波器和比较器阈值设对了吗?
  • 分治算法的递归深度控制与栈空间优化的技术8
  • Cursor Pro破解技术深度解析:从机器ID重置到多平台兼容的开源解决方案
  • IronyModManager:终极Paradox游戏模组冲突解决方案指南
  • WechatBakTool:微信聊天记录备份解密全攻略
  • Windows Cleaner终极指南:三步告别C盘爆红,免费开源工具助你重获流畅体验
  • 如何彻底掌控Mac睡眠模式?SleeperX让你的Mac按需休眠
  • 联想刃7000k BIOS隐藏选项解锁:三步实现高级配置权限提升
  • Typora自动编号插件:告别手动编号,实现文档结构化自动化
  • 别再死记硬背了!用Wireshark抓包实战,带你彻底搞懂TCP拥塞控制(慢开始/快恢复)