在这里插入图片描述
在这里插入图片描述

一、设计目标

本应用是 HarmonyOS 官方资源方案的演示器——前 11 个应用都用"运行时文案表"(STRINGS Map)做多语言,而本应用展示另一条正统路线:资源限定符目录 + $r() 引用。UI 设计要回答三个问题:

  1. 资源目录结构怎么展示才不枯燥:base/zh_CN/en_US/ja_JP/ko_KR 的树形关系是这套方案的心脏,用户必须先看懂它;
  2. 静态引用与动态读取的区别怎么可视化$r('app.string.xxx') 跟随系统语言自动匹配(零代码),resourceManager.getStringSync 按参数显式取——两种方式的差异要在 UI 上"看得见";
  3. 语言匹配规则怎么演示:选择某个语言时,资源表给出匹配结果;选择未覆盖语言时展示 base 兜底——降级路径本身就是教学内容

因此本页 UI 的核心原则是:把"资源匹配"变成一个可交互的实验台——用户拨动语言开关,看资源如何被选中、如何兜底,而不是只看静态文档。

二、页面信息架构(组件树)

ResourceQualifierPage(@Entry 根组件)
└─ Scroll(整页滚动,背景 #F8FAFC)
   └─ Column(宽 100%)
      ├─ Row:标题栏("多语言资源" + 副标题 + 🗂️ 图标)
      ├─ Flex:界面语言徽章组(简中/繁中/EN/日/韩,选中靛蓝高亮)
      ├─ Text + Column:资源目录结构卡(树形文本,monospace 靛蓝,浅靛底 #EEF2FF)
      │    └─ resources/ ├─ base ← 默认 ├─ zh_CN ├─ en_US ├─ ja_JP └─ ko_KR
      ├─ Text + Column:$r() 静态引用卡(白色)
      │    └─ 5 行:key(左灰,宽 110) + 当前语言资源值(右)
      ├─ Text + Column:语言匹配模拟器卡(浅靛底)
      │    ├─ Flex 徽章:zh_CN / en_US / ja_JP / ko_KR / base(🌐)
      │    └─ 匹配结果:5 行 key = 值(加粗靛蓝)
      └─ Text:底部提示(真实设备 $r() 自动切换,本页用动态读取模拟)

与前面应用的区别:本页没有独立的"内容区",四个区块全部围绕"资源如何被取用"展开——这是教学型演示 App 的典型结构:概念(目录)→ 机制(静态)→ 实验(模拟器)→ 结论(提示)。

三、色彩与字号规范

本页主题色为靛蓝色系(Indigo)——开发工具/官方文档的气质,与 09 橙、10 紫、11 深蓝区分:

元素 色值 字号 字重
页面标题 #1F2937 22 Bold
副标题 #9CA3AF 12 Normal
区块标题 #1F2937 14 Bold
目录树文本 #4F46E5 11 Normal(monospace)
资源 key #6B7280 / #9CA3AF 12 Normal
资源值(静态卡) #1F2937 13 Normal
资源值(模拟器) #4F46E5 14 Bold
选中徽章 文字 #FFFFFF / 底 #4F46E5 11 Normal
未选中徽章 文字 #374151 / 底 #E5E7EB 11 Normal
浅靛卡片背景 #EEF2FF

色彩语义延续系列:靛蓝 = 选中/强调、灰 = 未选中。两个紫色系(10 紫 #9333EA 与 12 靛蓝 #4F46E5)色相相邻但一暖一冷,同屏不混淆,跨应用一眼可辨。

四、资源目录结构卡:把"文件系统"画成 UI

Column({ space: 2 }) {
  ForEach(DIR_TREE, (line: string, idx: number) => {
    Text(line).fontSize(11).fontColor('#4F46E5').width('100%').fontFamily('monospace')
  }, (line: string, idx: number) => `${idx}-${line}`)
}
.width('94%').padding(14).backgroundColor('#EEF2FF').borderRadius(14).margin({ top: 8 })

展示内容:

resources/
├─ base/element/string.json   ← 默认(所有语言兜底)
├─ zh_CN/element/string.json ← 简体中文
├─ en_US/element/string.json ← 美式英语
├─ ja_JP/element/string.json ← 日语
└─ ko_KR/element/string.json ← 韩语

设计要点:

  • monospace 等宽字体 + 树形字符├─/└─ 是 ASCII 艺术,等宽字体保证对齐,一眼看出目录层级;
  • base 加 ← 默认 注释:直接点破"base 是兜底"这个最容易被误解的概念;
  • 靛蓝文字 on 浅靛底:整卡像一张"代码截图",与普通内容卡片形成视觉类型区分——用户一看就知道"这里是结构说明";
  • 真实工程对应entry/src/main/resources/ 下就是这样的目录,本卡是它的抽象镜像。

五、$r() 静态引用卡:自动匹配的演示

ForEach(RES_TABLE, (e: ResEntry) => {
  Row() {
    Text(e.key).fontSize(12).fontColor('#6B7280').width('110')
    Text(this.resFor(this.currentLocale, e.key))
      .fontSize(13).fontColor('#1F2937').layoutWeight(1)
  }.width('100%')
}, (e: ResEntry) => e.key)
  • 左侧固定宽 110 显示资源 key(app_name/greeting/btn_ok/btn_cancel/status_loading),右侧显示当前界面语言下的资源值;
  • key 与值分离:模拟的是 $r('app.string.app_name') 的语义——代码里写 key,运行时系统按语言解析;
  • 切换界面语言,整卡 5 行值即时变化——演示"$r() 在真实设备上随系统语言自动切换"的效果;
  • 静态卡用普通字重(区别于模拟器的加粗),因为它是"系统自动行为"的展示,不是用户可控实验。

六、语言匹配模拟器:用户可控的"实验台"

这是本页交互核心——用户可以指定"系统语言"并观察匹配结果:

Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Start }) {
  ForEach(['zh_CN', 'en_US', 'ja_JP', 'ko_KR', 'base'] as string[], (code: string) => {
    Text(this.langLabel(code))
      .fontSize(11).padding({ left: 8, right: 8, top: 5, bottom: 5 }).borderRadius(12)
      .backgroundColor(this.simLang === code ? '#4F46E5' : '#E5E7EB')
      .fontColor(this.simLang === code ? Color.White : '#374151')
      .onClick(() => { this.simLang = code; })
  }, (code: string) => code)
}.width('100%').margin({ top: 6 })

Text(`${this.T(K.matchResult)}:`).fontSize(12).fontColor('#6B7280').width('100%').margin({ top: 10 })
ForEach(RES_TABLE, (e: ResEntry) => {
  Row() {
    Text(`${e.key} =`).fontSize(12).fontColor('#9CA3AF').width('110')
    Text(this.resFor(this.simLang, e.key))
      .fontSize(14).fontWeight(FontWeight.Bold).fontColor('#4F46E5').layoutWeight(1)
  }.width('100%').margin({ top: 4 })
}, (e: ResEntry) => `sim-${e.key}`)

设计决策:

  • 5 个徽章含 🌐 base:把"兜底目录"做成一个可选项——用户选 base 时看到所有 key 回退到默认值,兜底机制变成主动实验而不是被动意外;
  • 模拟语言与界面语言独立simLang 是独立状态,界面语言切英文、模拟器仍可模拟日文系统——两个开关互相不污染,教学场景下能同时演示"系统语言 zh_CN + 界面语言 en_US"的组合;
  • 匹配结果行 key = 值app_name = 资源演示 用等号连接,加粗靛蓝——结果区永远是全卡视觉终点;
  • ForEach key 加 sim- 前缀:与静态卡的 key 区分,两个 ForEach 渲染同一 RES_TABLE 也不冲突。

七、底部提示:把"模拟"与"真实"的边界讲清楚

Text(this.T(K.hint))
  .fontSize(11).fontColor('#9CA3AF').width('90%').margin({ top: 12, bottom: 16 })
  .textAlign(TextAlign.Center)

提示文案:“真实设备上 $r() 会随系统语言自动切换;本页用动态读取模拟效果。”——教学型 App 的诚实条款:模拟器不会真去改系统语言,它只是复现了资源匹配的算法。这个提示防止用户误以为"改模拟器=改了系统",是演示工具应有的严谨。

八、排版验证清单(多语言下自查)

检查项 方法 验证结果
树形对齐 monospace 等宽字体 ├─└─ 字符不歪
key 列对齐 固定宽 110 5 行 key 左缘一致
静态卡随界面语言 resFor(currentLocale, key) 切英文整卡变英文
模拟器独立于界面语言 simLang 独立状态 界面中文 + 模拟日文互不干扰
base 兜底 resFor('base', key) 所有 key 回退默认值
长文案不溢出 layoutWeight(1) 日文"こんにちは、おかえりなさい"完整显示
窄屏不溢出 徽章 FlexWrap 5 个语言徽章自动折行

九、可访问性(多语言 UI 加分项)

  • 模拟器选中态三重编码:靛蓝底 + 白字 + 徽章文案本身(语言码),不单靠颜色;
  • 对比度:正文 #1F2937 ≈ 15:1、靛蓝 #4F46E5 on 白 ≈ 6.3:1、浅靛底 #EEF2FF 上靛蓝文字 ≈ 5.5:1,均满足 WCAG AA;
  • 树形文本可朗读├─ base/element/string.json ← 默认 的完整路径文本,朗读器按行读出,不依赖图形;
  • key 列固定宽度:屏幕朗读器按行读取时结构稳定,不会因换行错乱;
  • 提示文字居中且字号 11:作为"脚注"存在,不干扰主内容层级。

十、小结

本页 UI 把"资源限定符"从文档概念变成了可操作实验:目录树卡建立心智模型、静态引用卡演示自动行为、模拟器卡提供可控实验、底部提示划清模拟边界。四个区块层层递进,用户 30 秒内理解"base 兜底 + 语言限定符匹配"的完整机制。这套"概念 → 演示 → 实验 → 结论"的四段式布局,是任何教学型演示 App 的通用模板,也为第 14 应用(系统语言跟随)的"策略 + 模拟器"结构埋下了伏笔。下一篇文章深入技术实现,讲解 $r() 与 resourceManager 两种取资源的完整链路与匹配优先级算法。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐