这篇做一个字体实验室:把 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 秒;静态图保留完整手机界面,便于核对模式、暂停状态和实时数据。