HarmonyOS应用开发实战:猫猫大作战-`$r` 与 `$rawfile` 的区别、资源目录结构、多分辨率适配

前言

前面我们大量用 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>') 引用资源 ------typemedia/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/mediafiberscale-2x/mediafiberscale-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)

七、调试技巧

  1. DevEco 资源预览resources/base/media/ 下的图片在 IDE 里直接预览。
  2. console.info 打资源 id$r('app.media.cat_logo').id 打 log,追资源加载。
  3. 图片不显示排查 :检查资源名拼写;检查文件是否在 media/rawfile/;检查 Image 宽高是否设。
  4. 模糊排查:1x 设备加载 3x 图会缩放模糊,补全多分辨率资源。

八、性能与最佳实践

  1. UI 图标用 $r------编译期校验、多分辨率适配免费。
  2. **Lottie/字体/大文件用 rawfile**------r 不支持非标准资源。
  3. 资源名蛇形命名------驼峰会编译报错。
  4. 多分辨率准备 1x/2x/3x------避免高 dpi 设备加载低清图模糊。
  5. Image 必须设宽高------不设按原始像素渲染,可能撑爆屏幕。
  6. UI 图标 objectFit 用 Contain------保证完整不变形。

总结

本篇我们从 Image 资源引用切入,掌握了**r 编译期资源(多语言/多分辨率/暗色自动适配)**、**rawfile 原始文件(Lottie/字体/大文件)资源目录结构与命名规则三大要点,并给出了主菜单 PNG 图标改造完整代码。核心要点: r 编译期校验+多端适配;rawfile 任意文件;资源名蛇形命名;UI 图标准备 1x/2x/3x 三套**。

下一篇我们将拆解暗色模式适配------深色资源与 colorMode。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

相关推荐
youtootech13 小时前
HarmonyOS 实战教程(八):个人中心与华为云服务集成 —— 以「柚兔自测量表」为例
华为·华为云·harmonyos
红烧大青虫14 小时前
HarmonyOS应用《玄象》开发实战:掷钱动画:animateTo + 缓动曲线的物理感模拟
harmonyos·鸿蒙
二流小码农15 小时前
鸿蒙开发:实现文本渐变效果
android·ios·harmonyos
程序员黑豆16 小时前
鸿蒙应用开发:Refresh + List 下拉刷新组件使用教程
前端·华为·harmonyos
fengxinzi_zack17 小时前
HarmonyOS应用《玄象》开发实战:MansionListPage 列表页:List / ListItem / LazyForEach 性能优化
harmonyos·鸿蒙
youtootech17 小时前
HarmonyOS 实战教程(九):响应式布局与断点系统 —— 以「柚兔自测量表」为例
华为·harmonyos
山璞17 小时前
将一个 Flutter 项目转为用 ArkUI-X 框架实现(4)
flutter·harmonyos
2501_9197490318 小时前
华为鸿蒙隐私文件加密APP—小羊加密室
华为·harmonyos
程序员黑豆18 小时前
鸿蒙开发实战:使用 List 组件构建新闻列表
前端·华为·harmonyos