

前言
前面我们大量用 Emoji(🐱、🔥、🏆)做图标------简单但不专业。真正的游戏需要 PNG/JPG/SVG 图集:猫咪精灵图、UI 图标、背景插画。HarmonyOS 加载图片资源有两条路径------$r() 引用编译期资源 和**$rawfile() 引用原始文件**。两者用法、性能、适用场景完全不同。
本篇以「猫猫大作战」图标资源加载为锚点,把 $r 与 $rawfile 的区别、资源目录结构、多分辨率适配三大要点讲透。
提示:本系列不讲 ArkTS 基础语法与环境搭建,假设你已跟完第 1--26 篇。本篇是布局进阶的第七篇。
一、场景拆解:图标资源加载
回顾「猫猫大作战」主菜单标题图标(第 1 篇):
ts
// 来源:entry/src/main/ets/pages/Index.ets MainMenuView()
Text('🐱').fontSize(72)
这是用 Emoji 文字做图标。现在想换成专业的 PNG 图标 cat_logo.png------需要 Image 组件 + 资源引用:
ts
// 写法 1:$r() 引用编译期资源(推荐)
Image($r('app.media.cat_logo'))
.width(72).height(72)
// 写法 2:$rawfile() 引用原始文件
Image($rawfile('icons/cat_logo.png'))
.width(72).height(72)
关键区别:
| 维度 | $r('app.media.xxx') |
$rawfile('path/xxx') |
|---|---|---|
| 资源位置 | resources/base/media/ |
resources/rawfile/ |
| 编译处理 | 编译期索引、可多语言多分辨率 | 原样打包,不处理 |
| 引用方式 | 资源名(无扩展名) | 相对路径(含扩展名) |
| 适用 | UI 图标、标准图片 | 动效 JSON、字体、大文件 |
二、$r 编译期资源引用
2.1 资源目录结构
entry/src/main/resources/
├─ base/ # 默认资源(所有设备匹配)
│ ├─ element/ # string.json, color.json, float.json
│ ├─ media/ # 图片、音频、视频
│ │ ├─ cat_logo.png
│ │ ├─ icon_pause.png
│ │ └─ bg_main.jpg
│ └─ profile/ # 自定义 JSON 配置
├─ zh_CN/element/ # 中文语言覆盖
├─ en_US/element/ # 英文语言覆盖
├─ dark/element/ # 暗色模式覆盖
└─ rawfile/ # 原始文件(不编译)
└─ icons/cat_logo.png
2.2 $r 三种资源类型
ts
// 1. media:图片/音频/视频
Image($r('app.media.cat_logo'))
// 2. string:字符串
Text($r('app.string.hello_world'))
// 3. color/float:颜色/尺寸
Text('A').fontColor($r('app.color.primary'))
Text('B').fontSize($r('app.float.title_size'))
关键经验 :$r('app.<type>.<name>') 引用资源 ------type 是 media/string/color/float/plural 等,name 是资源名(不含扩展名)。
2.3 命名规则
ts
// ✅ 合法资源名
$r('app.media.cat_logo') // 蛇形命名
$r('app.media.icon_pause')
$r('app.string.hello_world')
// ❌ 非法资源名
$r('app.media.catLogo') // 驼峰,ArkTS 不允许
$r('app.media.cat-logo') // 含连字符
$r('app.media.cat logo') // 含空格
提示:HarmonyOS 资源名必须蛇形命名(snake_case)------驼峰会编译报错。
2.4 多分辨率适配
HarmonyOS 自动按设备密度加载对应 dpi 的图片:
resources/
├─ base/media/cat_logo.png # 默认(mdpi 1x)
├─ fiberscale-2x/media/cat_logo.png # 2x 高分辨率
└─ fiberscale-3x/media/cat_logo.png # 3x 超高分辨率
| 设备密度 | 加载的图片 |
|---|---|
| mdpi(1x) | base/media/cat_logo.png |
| xhdpi(2x) | fiberscale-2x/media/cat_logo.png |
| xxhdpi(3x) | fiberscale-3x/media/cat_logo.png |
实战经验 :UI 图标准备 1x、2x、3x 三套 ------分别放在 base/media、fiberscale-2x/media、fiberscale-3x/media,系统自动选最清晰的。
三、$rawfile 原始文件引用
3.1 适用场景
ts
// 1. Lottie 动效 JSON
Image($rawfile('lottie/loading.json'))
// 2. 自定义字体 TTF
Font().registerFont({ familyName: 'Custom', familySrc: $rawfile('fonts/Custom.ttf') })
// 3. 大型背景图(不想编译处理)
Image($rawfile('bg/level_1.jpg'))
// 4. 配置 JSON 文件
const config = fs.readSync($rawfile('config/game.json'))
3.2 路径规则
ts
$rawfile('icons/cat_logo.png')
// 相对 resources/rawfile/ 的路径
// 完整路径:resources/rawfile/icons/cat_logo.png
关键经验 :$rawfile 路径必须包含扩展名 ------与 $r 省略扩展名不同。
3.3 rawfile vs r 的核心差异
| 维度 | $r | $rawfile |
|---|---|---|
| 编译期索引 | ✅ 拼写错误编译报错 | ❌ 运行时才报错 |
| 多语言覆盖 | ✅ zh_CN//en_US/ 自动切 |
❌ 不支持 |
| 多分辨率 | ✅ fiberscale-2x/ 自动选 |
❌ 不支持 |
| 暗色模式 | ✅ dark/ 自动切 |
❌ 不支持 |
| 文件类型限制 | 限图片/音频/视频 | 任意文件 |
| 适用 | UI 图标、标准化资源 | Lottie、字体、大文件 |
实战经验 :能用 r 就用 r------编译期校验、多端适配都免费。只有 Lottie 动效、自定义字体这种「非标准资源」才用 $rawfile。
四、用 $r 改造主菜单图标
4.1 替换标题 Emoji 为 PNG 图标
假设已放置 resources/base/media/cat_logo.png:
ts
@Builder
MainMenuView() {
Column() {
Spacer().height('15%')
// 改造:Emoji → $r PNG 图标
Image($r('app.media.cat_logo'))
.width(72)
.height(72)
.margin({ bottom: 8 })
Text('猫猫大作战').fontSize(36).fontWeight(FontWeight.Bold).fontColor('#2C3E50').margin({ bottom: 8 })
Text('合并进化 · 策略消除').fontSize(16).fontColor('#95A5A6').margin({ bottom: 48 })
if (this.highScore > 0) {
Row() {
// 改造:🏆 Emoji → $r PNG 奖杯图标
Image($r('app.media.icon_trophy'))
.width(20).height(20)
Text(` 最高分: ${this.highScore}`).fontSize(16).fontColor('#F1C40F').fontWeight(FontWeight.Bold)
}.margin({ bottom: 32 })
}
// 开始游戏按钮
Button('开始游戏')
.width('70%').height(56)
.fontSize(20).fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF').backgroundColor('#2ECC71')
.borderRadius(28)
.shadow({ radius: 8, color: 'rgba(46, 204, 113, 0.4)', offsetY: 4 })
.onClick(() => { this.startGame(); })
Spacer().height(24)
// 规则面板(第 25 篇)
Scroll() { /* ... */ }.height(180)
Spacer()
}
.width('100%').height('100%')
.linearGradient({
direction: GradientDirection.Bottom,
colors: [['#E8F4F8', 0.0], ['#D6EEF5', 0.5], ['#C9E8F2', 1.0]]
})
.alignItems(HorizontalAlign.Center)
}
4.2 Image 组件常用属性
ts
Image($r('app.media.cat_logo'))
.width(72).height(72) // 尺寸
.objectFit(ImageFit.Contain) // 缩放模式
.borderRadius(12) // 圆角
.interpolation(ImageInterpolation.High) // 高质量插值
.draggable(false) // 禁止拖拽
.alt($r('app.media.placeholder')) // 加载失败占位图
4.3 objectFit 缩放模式
| ImageFit 值 | 效果 | 适用 |
|---|---|---|
Contain |
等比缩放,完整显示,可能留白 | UI 图标(推荐) |
Cover |
等比缩放,填满容器,可能裁剪 | 背景图 |
Fill |
拉伸填满,变形 | 不推荐 |
None |
原始尺寸,可能溢出 | 精确像素 |
ScaleDown |
等比缩小,不放大 | 缩略图 |
实战经验 :UI 图标用 Contain ------保证图标完整不变形;全屏背景用 Cover------填满屏幕不裁剪主体。
五、资源访问的编程式 API
5.1 resourceManager 获取资源
ts
import { common } from '@kit.AbilityKit';
const context = getContext(this) as common.UIAbilityContext;
const resMgr = context.resourceManager;
// 同步获取字符串
const str: string = resMgr.getStringSync($r('app.string.hello_world').id);
// 同步获取颜色
const color: number = resMgr.getColorSync($r('app.color.primary').id);
// 异步获取 rawfile 文件描述符
const rawFd = await resMgr.getRawFd('config/game.json');
// rawFd = { fd, offset, length }
5.2 getStringByNameSync 按名取值
ts
// 按资源名(不含 app. 前缀)取字符串
const greeting: string = resMgr.getStringByNameSync('hello_world');
实战经验 :UI 层用 $r() 直接渲染,逻辑层用 resourceManager 取值------例如要把字符串塞进 ArrayBuffer 时。
六、踩坑提示
6.1 资源名拼错
ts
// ❌ 错误:拼写错误,编译报错
Image($r('app.media.cat_Logo')) // L 大写
Image($r('app.media.catlogo')) // 缺下划线
// ✅ 正确:与文件名完全一致(蛇形)
Image($r('app.media.cat_logo'))
6.2 $r 引用 rawfile 资源
ts
// ❌ 错误:$r 不能引用 rawfile 下的文件
Image($r('app.media.cat_logo')) // 但 cat_logo.png 在 rawfile/icons/ 下
// ✅ 正确:rawfile 用 $rawfile
Image($rawfile('icons/cat_logo.png'))
6.3 多分辨率图片缺失
# ❌ 只有 3x 图,1x 设备加载 3x 图缩放,模糊
resources/base/media/ # 空
resources/fiberscale-3x/media/cat_logo.png
# ✅ 至少提供 1x 和 3x
resources/base/media/cat_logo.png # 1x
resources/fiberscale-3x/media/cat_logo.png # 3x
6.4 Image 不设宽高
ts
// ❌ 错误:不设尺寸,Image 按图片原始像素渲染,可能撑爆屏幕
Image($r('app.media.cat_logo'))
// ✅ 正确:显式设尺寸
Image($r('app.media.cat_logo')).width(72).height(72)
七、调试技巧
- DevEco 资源预览 :
resources/base/media/下的图片在 IDE 里直接预览。 console.info打资源 id :$r('app.media.cat_logo').id打 log,追资源加载。- 图片不显示排查 :检查资源名拼写;检查文件是否在
media/或rawfile/;检查 Image 宽高是否设。 - 模糊排查:1x 设备加载 3x 图会缩放模糊,补全多分辨率资源。
八、性能与最佳实践
- UI 图标用 $r------编译期校验、多分辨率适配免费。
- **Lottie/字体/大文件用 rawfile**------r 不支持非标准资源。
- 资源名蛇形命名------驼峰会编译报错。
- 多分辨率准备 1x/2x/3x------避免高 dpi 设备加载低清图模糊。
- Image 必须设宽高------不设按原始像素渲染,可能撑爆屏幕。
- UI 图标 objectFit 用 Contain------保证完整不变形。
总结
本篇我们从 Image 资源引用切入,掌握了**r 编译期资源(多语言/多分辨率/暗色自动适配)**、**rawfile 原始文件(Lottie/字体/大文件)、资源目录结构与命名规则三大要点,并给出了主菜单 PNG 图标改造完整代码。核心要点: r 编译期校验+多端适配;rawfile 任意文件;资源名蛇形命名;UI 图标准备 1x/2x/3x 三套**。
下一篇我们将拆解暗色模式适配------深色资源与 colorMode。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- 「猫猫大作战」项目源码:本仓库
entry/src/main/ets/pages/Index.ets - Image 组件官方指南
- 资源引用 r 与 rawfile 官方指南
- 资源目录结构官方文档
- 多分辨率资源适配最佳实践
- 开源鸿蒙跨平台社区
- HarmonyOS 开发者官方文档首页
- 系列索引:本仓库
articles/INDEX.md