鸿蒙学习实战之路-Reader Kit自定义字体最佳实践
最近好多朋友问我:"西兰花啊,我用Reader Kit构建了阅读器,可怎么才能让字体更好看呢?默认字体有点单调啊~" 害,这问题可问对人了!咱们前面做的阅读器就像一碗白米饭,现在终于要给它加点菜了~
今天这篇,我就手把手带你用Reader Kit实现自定义字体,教你怎么让你的阅读器字体变得更加美观,全程不超过8分钟(不含下载时间)~
一、自定义字体的基本流程
咱们先来看个流程图,了解一下整个过程是咋回事:

简单来说,整个流程就像咱们做饭的过程:
- 准备食材(导入相关模块)
- 选择调料(定义字体文件存放路径)
- 调配味道(设置自定义字体的名称及路径)
- 上菜(注册资源请求接口并返回资源)
二、需要用到的接口
咱们做这个功能,主要需要用到3个接口,就像做饭需要用到盐、糖、醋一样:
| 接口名 | 描述 |
|---|---|
| setPageConfig | 设置或者修改页面排版属性,相当于调整菜品的味道 |
| on('resourceRequest') | 注册资源请求回调,如果设置了自定义背景、字体时,排版引擎会通过此接口获取对应的资源,相当于厨师向食材库要材料 |
| off('resourceRequest') | 注销资源请求回调接口,可在页面销毁时调用,相当于用完调料要收好 |
三、开发准备
在开始自定义字体之前,咱们需要做一些准备工作,就像做饭前要先买菜一样:
- 已经构建了阅读器:咱们需要先按照之前的教程构建一个基本的阅读器,就像要先有碗才能盛饭一样~
- 准备好字体资源:咱们需要准备好想要使用的字体文件,比如思源宋体、思源黑体等,就像要先准备好调料一样~
字体文件可以放在两个地方:
- 工程目录
resources/rawfile文件夹 - 应用沙箱目录
四、具体实现步骤
1. 导入相关模块
首先,咱们得把需要的模块导入进来,就像做饭前先把所有食材和工具都摆出来一样:
typescript
import { fileIo as fs } from "@kit.CoreFileKit";
import { common } from "@kit.AbilityKit";
import { hilog } from "@kit.PerformanceAnalysisKit";
2. 定义字体文件存放路径
接下来,咱们需要定义字体文件的存放路径,就像要告诉厨师调料放在哪里一样:
- 若资源放在项目
resources/rawfile/fonts文件夹下:
typescript
let filePath: string = "fonts/SourceHanSerifCN-VF.ttf";
- 若资源放在应用沙箱目录下:
typescript
let filePath: string =
this.getUIContext().getHostContext()!.filesDir +
"/fonts/SourceHanSerifCN-VF.ttf";
3. 设置自定义字体
现在,咱们需要通过ReaderSetting的fontName和fontPath属性设置自定义字体的名称及所在的路径,并调用ReaderComponentController组件控制器的setPageConfig接口,重新渲染界面,就像在菜里加调料一样:
typescript
this.readerSetting.fontName = "思源宋体";
// 路径为上述两种之一
this.readerSetting.fontPath = filePath;
this.readerComponentController.setPageConfig(this.readerSetting);
4. 注册资源请求接口
最后,咱们需要注册排版引擎资源请求接口,并返回相应资源,就像厨师需要什么材料咱们就给他拿什么材料一样:
typescript
aboutToAppear(): void {
// 注册资源请求回调
this.readerComponentController.on('resourceRequest', this.resourceRequest);
}
aboutToDisappear(): void {
// 注销资源请求回调
this.readerComponentController.off('resourceRequest');
}
private isFont(filePath: string): boolean {
let options = [".ttf", ".woff2", ".otf"];
let path = filePath.toLowerCase();
let result = path.indexOf(options[0]) != -1 || path.indexOf(options[1]) != -1 || path.indexOf(options[2]) != -1;
hilog.info(0x0000, 'testTag', 'isFont = ' + result);
return result;
}
/**
* 资源请求回调
*/
private resourceRequest: bookParser.CallbackRes<string, ArrayBuffer> = (filePath: string): ArrayBuffer => {
hilog.info(0x0000, 'testTag', 'resourceRequest : filePath = ' + filePath);
if (filePath.length === 0) {
return new ArrayBuffer(0);
}
if (!this.isFont(filePath)) {
return new ArrayBuffer(0);
}
try {
let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
// 获取资源路径resources/rawfile/fonts下的字体文件Uint8Array数据
let value: Uint8Array = context.resourceManager.getRawFileContentSync(this.readerSetting.fontPath);
hilog.info(0x0000, 'testTag', 'resourceRequest : get other resource succeeded ');
return value.buffer as ArrayBuffer;
} catch (error) {
let code = (error as BusinessError).code;
let message = (error as BusinessError).message;
hilog.error(0x0000, 'testTag',
`resourceRequest : get other resource failed, error code: ${code}, message: ${message}.`);
}
// 如果在资源路径源路径resources/rawfile/fonts下获取字体文件数据失败,则去沙箱目录下获取字体文件数据
return this.loadFileFromPath(this.readerSetting.fontPath);
}
private loadFileFromPath(filePath: string): ArrayBuffer {
try {
let stats = fs.statSync(filePath);
let file = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
let buffer = new ArrayBuffer(stats.size);
fs.readSync(file.fd, buffer);
fs.closeSync(file);
return buffer;
} catch (err) {
hilog.error(0x0000, 'testTag', "mkdir failed with error message: ", err.message, ", error code: ", err.code);
return new ArrayBuffer(0);
}
}
五、需要注意的地方
🥦 西兰花警告
-
字体文件的格式 :代码中支持
.ttf、.woff2、.otf三种字体格式,其他格式可能会不兼容,就像不是所有的调料都适合放在一道菜里一样~ -
资源释放 :在
aboutToDisappear中,咱们必须调用off('resourceRequest')注销资源请求回调接口,否则会内存泄漏,就像做完饭要把调料收好一样,不然会有安全隐患~ -
错误处理 :代码中做了两层错误处理,先尝试从
resources/rawfile获取,失败后再尝试从沙箱目录获取,这样可以提高代码的健壮性,就像做饭时如果没有盐了可以用酱油代替一样,要有备选方案~
🥦 西兰花小贴士
-
字体文件的大小:字体文件可能会比较大,建议使用精简版的字体文件,或者只包含常用字符的字体文件,这样可以减少应用的体积和加载时间,就像做饭时调料要适量,放多了反而会影响味道~
-
字体的选择:不同的书籍类型适合不同的字体,比如小说适合用宋体,科技类书籍适合用黑体,就像不同的菜品需要不同的调料一样,要根据具体情况选择~
六、下一步行动
现在咱们已经学会了如何自定义字体,接下来可以试试:
- 实现自定义页面背景,让阅读体验更加个性化
- 实现字体大小、颜色的动态调整
- 实现多种字体的切换功能
📚 推荐资料
我是盐焗西兰花,
不教理论,只给你能跑的代码和避坑指南。
下期见!🥦