HarmonyOS SymbolGlyph 实战:从 Unicode 字符迁移到语义化系统图标
2026/8/24 4:51:01 网站建设 项目流程

Unicode 图标为什么适合原型、不适合长期数据

项目早期为了快速验证功能,曾经用 Unicode 字符表示图标:

☀ ☾ ✓ ♥ ♫

优点很明显:

  • 不需要准备图片资源;
  • Text 组件就能显示;
  • 写几个字符即可覆盖原型。

但进入正式版本后,问题也会出现:

  • 不同字体的字形差异很大;
  • 某些字符在不同设备上基线不一致;
  • 字符可能被渲染为彩色 Emoji;
  • 图标粗细无法和系统风格统一;
  • 持久化后很难知道“这个字符的业务含义是什么”;
  • 更换图标体系时需要迁移旧数据。

因此 HarmonyOS 版本最终使用SymbolGlyph和稳定语义 Key。

一、磁盘里保存语义,不保存 Resource 对象

习惯模型中的图标字段是字符串:

exportinterfaceHabit{id:string;name:string;icon:string;color:string;// 其他字段}

但这里的字符串不再是“☾”,而是:

moon_z figure_run book drop checkmark_circle

这些值是应用自己的语义协议。

好处是:

  • 不依赖某个具体系统资源对象能否 JSON 序列化;
  • 业务数据可以跨版本保存;
  • 将来替换成自定义图标时不必重写 Habit;
  • 中英日切换不影响图标身份;
  • 导出文件仍然可读。

二、通过一层映射连接业务 Key 与系统 Symbol

页面集中维护:

privateiconResource(icon:string):Resource{switch(normalizedIconKey(icon)){case'sun_max':return$r('sys.symbol.sun_max');case'face_smiling':return$r('sys.symbol.face_smiling');case'water_waves':return$r('sys.symbol.water_waves');case'moon_z':return$r('sys.symbol.moon_z');case'figure_run':return$r('sys.symbol.figure_run');case'book':return$r('sys.symbol.book');default:return$r('sys.symbol.circle');}}

这层映射看起来比直接在页面写$r()多了一步,却带来清晰边界:

业务数据:moon_z → 映射层:sys.symbol.moon_z → UI:SymbolGlyph

系统资源名变化或设计决定替换图标时,只需调整映射层。

三、封装统一的图标 Builder

项目用一个 Builder 统一尺寸和颜色入口:

@BuilderprivateAppIcon(icon:string,size:number,color:string){SymbolGlyph(this.iconResource(icon)).fontSize(size).fontColor([color])}

圆形底图也进一步封装:

@BuilderprivateIconBadge(icon:string,size:number,color:string,diameter:number,background:string){Stack({alignContent:Alignment.Center}){this.AppIcon(icon,size,color)}.width(diameter).height(diameter).backgroundColor(background).borderRadius(diameter/2)}

这样心情、标签、习惯和设置页不会各自维护一套 Symbol 样式。

四、旧 Unicode 数据必须在读取路径中兼容

应用一旦发布,磁盘数据就是需要长期兼容的协议。

不能把编辑器中的 Unicode 替换成新 Key 后,就假设旧用户数据自动变化。

项目提供归一化函数:

exportfunctionnormalizedIconKey(icon:string):string{switch(icon){case'☀':return'sun_max';case'≈':return'water_waves';case'☾':return'moon_z';case'▣':return'briefcase';case'↗':return'figure_run';case'◇':return'drop';case'▤':return'book';case'✓':return'checkmark_circle';case'♥':return'heart';case'♫':case'♬':return'music';default:returnicon;}}

显示图标和打开习惯编辑器时都先归一化:

this.habitIconDraft=normalizedIconKey(habit?.icon??HABIT_ICONS[0]);

这样旧数据仍能显示,用户下次保存习惯时会自然迁移到新的语义 Key。

五、懒迁移和一次性迁移怎么选

当前项目使用“读取兼容、编辑时迁移”的懒迁移。

适合:

  • 旧格式数量很少;
  • 映射完全确定;
  • 不迁移也不影响显示;
  • 数据规模小。

另一种方式是在 Repository 加载后一次性改写:

parsed.habits=parsed.habits.map((habit)=>({...habit,icon:normalizedIconKey(habit.icon)}));

然后保存新版本。

一次性迁移适合:

  • 后续代码不希望长期携带兼容分支;
  • 新旧格式混用会造成错误;
  • 迁移结果可验证;
  • 失败时有回滚策略。

无论选择哪种,都不应该直接把未知值清空。

六、未知图标需要稳定回退

导入文件、测试版本或未来资源变化可能带来未知 Key。

映射层默认返回:

return$r('sys.symbol.circle');

回退图标的目标不是“看起来完美”,而是保证:

  • 页面不会因为单个坏值崩溃;
  • 用户仍能打开编辑器并重新选择;
  • 导出和其他数据不受影响;
  • 问题可以通过日志或测试定位。

七、图标颜色也应该是数据的一部分吗

当前习惯保存:

color:'#5E7CE2'

这让用户选择的颜色能够持久化,但也意味着设计系统调整时,旧数据仍保留旧色值。

可以有两种模型:

保存实际色值

适合允许用户自由选择颜色,导出结果直观。

保存语义色 Key

例如:

habit-blue habit-mint habit-gold

适合需要统一适配深色模式或未来更换主题的产品。

当前项目色板固定且规模较小,直接保存色值足够。如果后续增加深色模式和主题系统,语义色 Key 会更易维护。

八、Symbol 不能替代无障碍名称

一个“锁”图标对开发者很直观,但屏幕阅读器需要知道它代表“应用锁”还是“隐私政策”。

因此可点击 Symbol 应与明确文本、控件标签或无障碍描述结合,不能只依靠图形表达业务操作。

尤其要避免:

  • 多个只显示图标的按钮没有名称;
  • 颜色是区分状态的唯一方式;
  • 选中态只改变图标粗细;
  • 删除和归档使用过于相似的图形。

九、系统 Symbol 的测试清单

  • 所有配置 Key 都能映射到资源;
  • 未知 Key 显示回退圆形;
  • 旧 Unicode 数据能打开和编辑;
  • 不同字号下图标没有截断;
  • 选中和未选中颜色对比清楚;
  • 中文、英文、日文下布局一致;
  • 真机系统版本包含使用到的 Symbol;
  • 重要操作有文字或无障碍名称;
  • 导出再导入后语义 Key 不丢失。

总结

从 Unicode 字符迁移到 SymbolGlyph,真正重要的不是“图标更好看”,而是建立了一套稳定协议:

  1. 磁盘保存业务语义 Key;
  2. 映射层连接系统 Symbol;
  3. Builder 统一视觉样式;
  4. 旧数据通过归一化函数兼容;
  5. 未知值有安全回退;
  6. 图标仍需要无障碍语义。

这套模式同样适用于系统图标、自定义 SVG 和图片资源之间的后续替换。

本文案例来自“心晴手记(MoodMemoir)”HarmonyOS 版的心情、标签与习惯图标系统。

参考资料

  • HarmonyOS 图标小符号 SymbolGlyph/SymbolSpan

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询