前言
前面我们大量用 Emoji(🐱、🔥、🏆)做图标——简单但不专业。真正的游戏需要 PNG/JPG/SVG 图集:猫咪精灵图、UI 图标、背景插画。HarmonyOS 加载图片资源有两条路径——$r()引用编译期资源和**$rawfile()引用原始文件**。两者用法、性能、适用场景完全不同。
本篇以「猫猫大作战」图标资源加载为锚点,把$r与$rawfile的区别、资源目录结构、多分辨率适配三大要点讲透。
提示:本系列不讲 ArkTS 基础语法与环境搭建,假设你已跟完第 1–26 篇。本篇是布局进阶的第七篇。
一、场景拆解:图标资源加载
回顾「猫猫大作战」主菜单标题图标(第 1 篇):
// 来源:entry/src/main/ets/pages/Index.ets MainMenuView() Text('🐱').fontSize(72)这是用 Emoji 文字做图标。现在想换成专业的 PNG 图标cat_logo.png——需要 Image 组件 + 资源引用:
// 写法 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.png2.2 $r 三种资源类型
// 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 命名规则
// ✅ 合法资源名 $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 适用场景
// 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 路径规则
$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:
@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 组件常用属性
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 获取资源
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 按名取值
// 按资源名(不含 app. 前缀)取字符串 const greeting: string = resMgr.getStringByNameSync('hello_world');实战经验:UI 层用$r()直接渲染,逻辑层用resourceManager取值——例如要把字符串塞进 ArrayBuffer 时。
六、踩坑提示
6.1 资源名拼错
// ❌ 错误:拼写错误,编译报错 Image($r('app.media.cat_Logo')) // L 大写 Image($r('app.media.catlogo')) // 缺下划线 // ✅ 正确:与文件名完全一致(蛇形) Image($r('app.media.cat_logo'))6.2 $r 引用 rawfile 资源
// ❌ 错误:$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 # 3x6.4 Image 不设宽高
// ❌ 错误:不设尺寸,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