第1.7篇:资源管理与国际化------$r 与 resource 体系
难度 :⭐ 入门 | 前置知识:1.2 ArkUI 声明式 UI 基础
涉及源文件:
products/default/src/main/ets/pages/Index.etsproducts/default/src/main/ets/constants/CommonConstants.etsproducts/default/src/main/ets/viewmodel/CatalogueItemData.ets

1. 引言
在鸿蒙应用中,图片、字符串、颜色、多媒体等统称为"资源"。ArkUI 提供了一套完善的资源管理体系,支持编译时资源检查、多设备适配和多语言国际化。本篇将以「画伴梦工厂」项目为例,介绍 $r()、$rawfile() 和 Resource 类型的用法。
2. resources 目录结构
鸿蒙应用的资源文件统一存放在 resources 目录下,按类型和设备类型组织:
resources/
├── base/ # 默认资源(所有设备通用)
│ ├── element/ # 基础元素(字符串、颜色、浮点数等 JSON)
│ │ ├── string.json
│ │ ├── color.json
│ │ └── float.json
│ ├── media/ # 图片/音频等媒体文件
│ │ ├── hero_portal.png
│ │ ├── fantasy_world.svg
│ │ └── kid_dino_sketch.svg
│ └── profile/ # 配置文件
│ ├── main_pages.json
│ └── form_config.json
├── dark/ # 深色模式覆盖
│ └── element/
│ └── color.json
├── en/ # 英文国际化(若有)
│ └── element/
│ └── string.json
└── rawfile/ # 原始文件(不经过编译处理)
└── index/
├── demo1.png
├── demo1.mp4
└── ...
关键概念:
base目录是兜底的资源目录,所有设备都会加载dark、en等限定词目录只在对应条件下覆盖 base 中的同名字资源media下的资源会被编译系统处理(压缩、格式转换等)rawfile下的文件保持原始状态,不做任何处理
3. $r() 与 $rawfile() 的使用
ArkUI 提供了两种引用资源的方式:$r() 和 $rawfile()。
3.1 $r():引用编译时资源
$r() 用于引用 element 和 media 目录下的资源,语法为 $r('app.type.name'):
typescript
// 引用 media 下的图片
Image($r('app.media.hero_portal'))
.width('100%')
.objectFit(ImageFit.Cover)
.borderRadius(26)
// 引用 element/string.json 中的字符串
Text($r('app.string.module_desc'))
// 引用 element/color.json 中的颜色
.backgroundColor($r('app.color.primary'))
$r() 返回的是 Resource 类型,它携带了资源的 ID 而非实际值。这意味着:
- 编译时检查:如果资源不存在,编译就会报错
- 多设备适配:系统会根据当前设备自动加载对应限定词的资源
- 延迟解析:资源值在运行时才解析,支持主题切换
3.2 $rawfile():引用原始文件
$rawfile() 用于引用 rawfile 目录下的资源,这类资源不会被编译处理:
typescript
// 引用 rawfile 下的演示图片
private getWorkCover(index: number): Resource | string {
switch (index) {
case 0: return $rawfile('index/demo1.png');
case 1: return $rawfile('index/demo2.png');
case 2: return $rawfile('index/demo3.png');
case 3: return $rawfile('index/demo4.png');
default: return $rawfile('index/demo5.png');
}
}
// 引用 rawfile 下的演示视频
private getWorkVideo(index: number): Resource | string {
switch (index) {
case 0:
case 3: return $rawfile('index/demo1.mp4');
case 1:
case 4: return $rawfile('index/demo2.mp4');
default: return $rawfile('index/demo3.mp4');
}
}
3.3 $r() vs $rawfile() 对比
| 对比维度 | $r() |
$rawfile() |
|---|---|---|
| 资源目录 | media/、element/ |
rawfile/ |
| 编译处理 | 压缩、格式转换 | 保持原样 |
| 多语言适配 | 自动根据限定词加载 | 不支持 |
| 深色模式切换 | 支持(通过 color.json) | 不支持 |
| 类型返回 | Resource |
Resource |
| 使用场景 | 图标、字符串、颜色 | 视频、PDF、GLTF 模型 |
| 编译时检查 | 检查资源是否存在 | 不检查 |
4. Resource 类型在数据模型中的应用
Resource 类型可以像普通类型一样用于数据模型字段,这使得组件能够延迟解析资源值:
typescript
// CatalogueItemData.ets
export class CatalogueItemData {
id: number = 0;
title: Resource = $r('app.string.responsive_layout'); // Resource 类型字段
uri: string = '';
params?: string;
constructor(id: number, title: Resource, uri: string, params?: string) {
this.id = id;
this.title = title;
this.uri = uri;
this.params = params;
}
}
在组件中直接使用:
typescript
// 假设有一个 GridItem 接收 CatalogueItemData
GridItem() {
Text(item.title) // title 是 Resource 类型,Text 组件原生支持
.fontSize(14)
}
注意 :
Resource类型不是string,不能直接用+拼接或模板字符串。如果需要展示资源对应的文本内容,可以使用getContext().resourceManager.getStringSync()解析。
5. 项目中资源使用规模统计
在「画伴梦工厂」项目中,资源体系的使用情况:
media 资源(通过 $r() 引用):
| 资源名称 | 类型 | 用途 |
|---|---|---|
hero_portal.png |
PNG | 首页 Hero 区背景图 |
fantasy_world.svg |
SVG | 我的世界页面插图 |
fantasy_worldNew.png |
PNG | 世界页顶部大图 |
kid_dino_sketch.svg |
SVG | 画板区示例涂鸦 |
dino_animation.svg |
SVG | 世界故事区插图 |
tab1.svg ~ tab4.svg |
SVG | Tab 图标 |
background.png |
PNG | 背景图片 |
startIcon.png |
PNG | 启动图标 |
rawfile 资源(通过 $rawfile() 引用):
| 文件 | 类型 | 用途 |
|---|---|---|
index/demo1.png ~ demo5.png |
PNG | 5 个演示作品的封面图 |
index/demo1.mp4 ~ demo5.mp4 |
MP4 | 演示作品的动画视频 |
assets/gltf/car.glb |
GLB | 3D 模型测试文件 |
element 资源:
| JSON 文件 | 内容 |
|---|---|
string.json |
应用名称、权限说明、布局页面标题等 14 条字符串 |
color.json |
颜色常量 |
float.json |
浮点数常量 |
dark/element/color.json |
深色模式颜色覆盖 |
6. 通过 resources 实现国际化
鸿蒙的资源系统天然支持国际化。实现思路非常简单:
6.1 目录结构
resources/
├── base/element/string.json # 默认语言(中文)
├── en/element/string.json # 英文覆盖
├── ja/element/string.json # 日文覆盖
└── ...
6.2 字符串定义
在 base/element/string.json 中定义默认(中文)字符串:
json
{
"string": [
{
"name": "module_desc",
"value": "module description"
},
{
"name": "ability_label",
"value": "画伴梦工厂"
},
{
"name": "camera_reason",
"value": "用于拍摄儿童的绘画作品,在本地完成识别和动画生成。"
},
{
"name": "microphone_reason",
"value": "用于录制语音描述,辅助创作生成。"
}
]
}
在 en/element/string.json 中提供英文版本:
json
{
"string": [
{
"name": "module_desc",
"value": "module description"
},
{
"name": "ability_label",
"value": "Drawing Dream Factory"
},
{
"name": "camera_reason",
"value": "Used to capture children's drawings for local recognition and animation generation."
},
{
"name": "microphone_reason",
"value": "Used to capture voice descriptions for creative generation."
}
]
}
6.3 代码中使用
在代码中始终通过 $r() 引用字符串资源,系统会根据当前语言环境自动选择:
typescript
// 不需要写 if-else,系统自动切换
Text($r('app.string.ability_label'))
.fontSize(20)
.fontWeight(FontWeight.Bold)
注意 :
string.json中的name必须全局唯一,建议使用模块名_用途的命名风格。
7. 项目中的主题色管理
除了字符串和图片,项目中还大量使用了组件级常量来管理颜色主题:
typescript
// Index.ets 中的组件内常量
private readonly brandPurple: string = '#7657F3';
private readonly ink: string = '#1E2442';
private readonly mint: string = '#42CDA3';
private readonly sunshine: string = '#FFB84D';
private readonly softBackground: string = '#F7F5FF';
这些常量在 @Builder 中广泛使用:
typescript
@Builder
private NavIcon(index: number) {
Column() {
Text(NAV_ICONS[index])
.fontColor(this.isPrimaryTabActive(index) ? this.brandPurple : '#8E94A6')
Text(NAV_ITEMS[index])
.fontColor(this.isPrimaryTabActive(index) ? this.brandPurple : '#747A92')
}
.backgroundColor(this.isPrimaryTabActive(index) ? '#FFFFFF' : Color.Transparent)
}
对比 :把颜色放在
element/color.json中通过$r()引用,可以实现深色模式自动切换。组件内常量则适合那些在各组件间共享、但不需要随主题变化的颜色值。
8. 小结
本篇学习了鸿蒙资源管理体系的核心内容:
| 知识点 | 说明 |
|---|---|
resources 目录结构 |
base/限定词 + element/media/rawfile |
$r('app.type.name') |
引用编译时资源,支持多语言和主题切换 |
$rawfile('path/file') |
引用原始文件,保持文件原样 |
Resource 类型 |
可延迟解析的资源引用,可用作数据模型字段 |
| 国际化实现 | 添加对应语言的限定词目录,覆盖 string.json |
| 组件内常量 | 管理主题色等不随语言/主题变化的值 |
下篇预告 :第1.8篇 组合式 API 实践------从零搭建一个 Tab 主页。我们将综合运用前面 7 篇文章的知识,分析 Index.ets 如何组合使用 @State、@Builder、Router、多 Tab 切换等构建完整主页。
思考题 :项目中 Index.ets 里有大量硬编码的中文文本(如
'首页'、'创作'),如果要做国际化,需要把这些文本都迁移到string.json中。你能估算一下,完成全面的国际化大概需要迁移多少条字符串吗?