HarmonyOS ArkGraphics 2D 自定义字体实操:注册字体并验证中文回退

这篇做一个字体实验室:把 OTF 字体放进 rawfile,注册成 InterDisplay,再用 TextStyle.fontFamilies 切换自定义字体、系统字体和中英混排回退链。先看真机动态效果,再看实现。

真机结果是一个完整的字体预览卡片:英文标题使用注册的 Inter 字体,中文和 Emoji 在字体缺字时由 HarmonyOS Sans 接管;页面底部显示当前段落宽度、行数和绘制次数。

先看结果

注册字体

进入首页的"实验 24:FONTLAB 字体与回退实验室",默认模式是 Inter Display。状态条显示 InterDisplay 已注册,画布中显示 FONT COLLECTION / FALLBACK CHAIN

切换回退链

点击"Fallback Mix",英文标题继续使用 InterDisplay,中文说明和 Emoji 沿回退链查找系统字体,卡片宽度和换行结果由 Paragraph 重新计算。

暂停和恢复

点击"暂停预览"后画布保留在当前相位,状态变为 PAUSED;点击"继续预览"后光标和进度条继续移动。

准备

  • DevEco Studio,API 26 工程。
  • 一台已连接的 HarmonyOS 真机;本次设备为 HUAWEI Mate 60 Pro,HarmonyOS 7.0。
  • 一份 TTF 或 OTF 字体。本次把 Inter-Regular.otf 放到 entry/src/main/resources/rawfile/
  • 页面入口:entry/src/main/ets/features/graphics2d/CustomFontFallbackPage.ets

官方文本能力说明:FontCollection 支持通过 loadFontSync() 注册字体,注册名需要写入 TextStyle.fontFamilies 才会参与排版。

实施过程

1. 把字体放入 rawfile

工程目录如下:

text 复制代码
entry/src/main/resources/rawfile/Inter-Regular.otf

rawfile 会随 HAP 一起打包,运行时不需要读取电脑路径,也不需要网络下载字体。

2. 注册字体别名

页面出现时用 FontCollection 注册别名。这里使用同步接口,注册完成后再启动 Canvas 动画:

ArkTS/ets 复制代码
const FONT_ALIAS: string = 'InterDisplay';
const collection: text.FontCollection = text.FontCollection.getGlobalInstance();

collection.loadFontSync(FONT_ALIAS, $rawfile('Inter-Regular.otf'));
hilog.info(DOMAIN, TAG,
  'FONT_READY alias=%{public}s rawfile=true fallback=true expectedFps=%{public}d',
  FONT_ALIAS, 30);

如果字体文件不存在,loadFontSync() 会抛出异常,页面状态改为 FONT LOAD ERROR,不会把"已注册"写成成功结果。

3. 设置字体链

三个按钮实际对应三组 fontFamilies

ArkTS/ets 复制代码
const families: Array<string> = modeIndex === 1
  ? ['HarmonyOS Sans']
  : ['InterDisplay', 'HarmonyOS Sans'];

const paragraphStyle: text.ParagraphStyle = {
  textDirection: text.TextDirection.LTR,
  align: text.TextAlign.LEFT,
  wordBreak: text.WordBreak.BREAK_WORD,
  breakStrategy: text.BreakStrategy.BALANCED,
  maxLines: 4,
  lineSpacing: 12,
  textStyle: {
    color: this.color(0xFFEAF4FF),
    fontSize: 24,
    fontWeight: text.FontWeight.W400,
    fontFamilies: families,
    locale: 'zh-Hans'
  }
};

InterDisplay 没有某个中文字符时,Paragraph 会继续尝试 HarmonyOS Sans。因此英文和中文可以留在一个 Paragraph 中,不需要手工判断每个字符属于哪套字体。

4. 混排标题、中文和数字

标题和正文仍然通过 pushStyle() 分层加入,局部样式共用同一组字体链:

ArkTS/ets 复制代码
builder.pushStyle({
  color: this.color(0xFF69F4D5),
  fontSize: 38,
  fontWeight: text.FontWeight.W700,
  fontFamilies: families,
  letterSpacing: 1.5,
  locale: 'en-Latn'
});
builder.addText('INTER DISPLAY');
builder.popStyle();

builder.pushStyle({
  color: this.color(0xFFD6E4F4),
  fontSize: 23,
  fontFamilies: families,
  locale: 'zh-Hans'
});
builder.addText('\n标题使用注册字体,中文与 Emoji 交给回退链。');
builder.addText('\nHarmonyOS 7  ·  API 26  ·  2026');
builder.popStyle();

5. 在 RenderNode 中测量并绘制

每次 RenderNode.draw() 都重新布局当前宽度,再将 Paragraph 绘制到 Canvas,并把真实宽度和行数同步到页面:

ArkTS/ets 复制代码
paragraph.layoutSync(contentWidth);
paragraph.paint(canvas, x, y);

const width: number = paragraph.getMaxWidth();
const lines: number = paragraph.getLineCount();
this.metrics = `宽度 ${Math.round(width)} px  ·  ${lines} 行`;

真机默认模式记录 宽度 963 px · 4 行;切换回退链后仍然保持 4 行,但段落中英文字符的字形来源发生变化。

6. 卸载字体

字体不能在页面退出后一直挂在全局集合中。退出时先取消 Animator、释放 RenderNode,再卸载别名:

ArkTS/ets 复制代码
private releaseAll(reason: string): void {
  if (this.released) {
    return;
  }
  this.released = true;
  this.animator?.cancel();
  this.controller.release();
  if (this.loaded) {
    text.FontCollection.getGlobalInstance().unloadFontSync(FONT_ALIAS);
    this.loaded = false;
  }
  hilog.info(DOMAIN, TAG, 'FONT_RELEASE reason=%{public}s', reason);
}

退出后的首页结果:

遇到的情况与处理

自定义字体注册成功但没有生效

只调用 loadFontSync() 不够,Paragraph 的 TextStyle.fontFamilies 还必须写入同一个别名。本实验把 InterDisplay 放在字体链第一位,英文标题即可看到注册字体效果。

中文显示成方框

Inter-Regular 只覆盖拉丁字符,缺少中文时不能把它作为唯一字体。将 HarmonyOS Sans 放在第二位后,中文和 Emoji 能继续由系统字体绘制,这就是本实验的回退模式。

重进页面后字体状态异常

FontCollection 是全局集合,页面退出不卸载会影响下一次实验。现在进入时注册、退出时 unloadFontSync(),每次重新进入都从干净状态开始。

真机 Hilog

包名、进程号和时间戳已删除,只保留本次验证的关键字段:

text 复制代码
FONT_LAB_ENTER modes=inter,system,fallback expectedFps=30 permissionRequired=false
FONT_READY alias=InterDisplay rawfile=true fallback=true expectedFps=30
FONT_DRAW mode=0 drawCount=30 width=1174 paragraphWidth=963 lines=4 alias=InterDisplay
FONT_MODE mode=2 name=Fallback Mix
FONT_PAUSE mode=2
FONT_RESUME mode=2
FONT_RELEASE reason=EXIT_BUTTON

FONT_READY 证明 rawfile 注册成功,FONT_MODE 证明回退模式切换成功,FONT_PAUSE/RESUME 与页面状态截图对应,FONT_RELEASE 证明退出时完成清理。

验证结果

验证项 真机结果
rawfile 注册 InterDisplay 注册成功
英文标题 使用 InterDisplay 绘制
中文与 Emoji HarmonyOS Sans 回退绘制
段落布局 paragraphWidth=963,4 行
动画控制 暂停后保持,恢复后继续
资源释放 退出日志记录 FONT_RELEASE,字体别名卸载

本次文件

text 复制代码
entry/src/main/ets/features/graphics2d/CustomFontFallbackPage.ets
entry/src/main/ets/pages/Index.ets
entry/src/main/resources/rawfile/Inter-Regular.otf
articles/第二十二篇-ArkGraphics 2D自定义字体实战/
docs/qa/fontlab-22/动态原始帧/
articles/第二十二篇-ArkGraphics 2D自定义字体实战/真机验证日志.txt

动态 GIF 使用 24 帧真机连续画面制作,规格为 720×610、8 FPS、3 秒;静态图保留完整手机界面,便于核对模式、暂停状态和实时数据。

相关推荐
不爱吃糖的程序媛3 小时前
从 0 到 1:react-native-transformer-text-input 鸿蒙化适配实录
react native·transformer·harmonyos
不爱吃糖的程序媛3 小时前
React Native 三方库鸿蒙适配实战:react-native-emoji-popup(Fabric 自定义组件)从 0 到 1
react native·harmonyos·fabric
贾伟康3 小时前
【口算王|19】HarmonyOS ArkTS 回归测试实战:覆盖启动、空数据、异常输入和重复点击
软件测试·harmonyos·arkts·回归测试·hypium
贾伟康3 小时前
【句匠|09】HarmonyOS ArkTS 学习统计实战:汇总正确率、连续学习和薄弱类型
harmonyos·arkts·appstorage·学习统计·本地统计
贾伟康3 小时前
【口算王|17】HarmonyOS ArkTS 亮暗色与视觉令牌实战:集中颜色、间距和交互状态避免页面割裂
harmonyos·arkts·arkui·深色模式·ui设计
搬砖的kk4 小时前
从 0 到 1:react-native-screenshot-aware 鸿蒙适配实战(RNOH 0.84)
react native·华为·harmonyos
贾伟康4 小时前
【口算王|15】HarmonyOS ArkTS 本地状态持久化实战:让保存、删除和页面返回后的数据即时一致
harmonyos·arkts·数据持久化·状态管理·preferences
贾伟康4 小时前
【口算王|18】HarmonyOS ArkTS 权限与隐私实战:让 module.json5、功能说明和拒绝路径一致
harmonyos·arkts·权限管理·隐私合规·module.json5
ChinaDragonDreamer4 小时前
HarmonyOS:6.0 新增和增强特性
harmonyos·鸿蒙
Georgewu4 小时前
【HarmonyOS AI】 通用文字识别详解
harmonyos