鸿蒙学习实战之路-Reader Kit自定义字体最佳实践

鸿蒙学习实战之路-Reader Kit自定义字体最佳实践

最近好多朋友问我:"西兰花啊,我用Reader Kit构建了阅读器,可怎么才能让字体更好看呢?默认字体有点单调啊~" 害,这问题可问对人了!咱们前面做的阅读器就像一碗白米饭,现在终于要给它加点菜了~

今天这篇,我就手把手带你用Reader Kit实现自定义字体,教你怎么让你的阅读器字体变得更加美观,全程不超过8分钟(不含下载时间)~


一、自定义字体的基本流程

咱们先来看个流程图,了解一下整个过程是咋回事:

简单来说,整个流程就像咱们做饭的过程:

  1. 准备食材(导入相关模块)
  2. 选择调料(定义字体文件存放路径)
  3. 调配味道(设置自定义字体的名称及路径)
  4. 上菜(注册资源请求接口并返回资源)

二、需要用到的接口

咱们做这个功能,主要需要用到3个接口,就像做饭需要用到盐、糖、醋一样:

接口名 描述
setPageConfig 设置或者修改页面排版属性,相当于调整菜品的味道
on('resourceRequest') 注册资源请求回调,如果设置了自定义背景、字体时,排版引擎会通过此接口获取对应的资源,相当于厨师向食材库要材料
off('resourceRequest') 注销资源请求回调接口,可在页面销毁时调用,相当于用完调料要收好

三、开发准备

在开始自定义字体之前,咱们需要做一些准备工作,就像做饭前要先买菜一样:

  1. 已经构建了阅读器:咱们需要先按照之前的教程构建一个基本的阅读器,就像要先有碗才能盛饭一样~
  2. 准备好字体资源:咱们需要准备好想要使用的字体文件,比如思源宋体、思源黑体等,就像要先准备好调料一样~

字体文件可以放在两个地方:


四、具体实现步骤

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);
  }
}

五、需要注意的地方

🥦 西兰花警告

  1. 字体文件的格式 :代码中支持.ttf、.woff2、.otf三种字体格式,其他格式可能会不兼容,就像不是所有的调料都适合放在一道菜里一样~

  2. 资源释放 :在aboutToDisappear中,咱们必须调用off('resourceRequest')注销资源请求回调接口,否则会内存泄漏,就像做完饭要把调料收好一样,不然会有安全隐患~

  3. 错误处理 :代码中做了两层错误处理,先尝试从resources/rawfile获取,失败后再尝试从沙箱目录获取,这样可以提高代码的健壮性,就像做饭时如果没有盐了可以用酱油代替一样,要有备选方案~

🥦 西兰花小贴士

  • 字体文件的大小:字体文件可能会比较大,建议使用精简版的字体文件,或者只包含常用字符的字体文件,这样可以减少应用的体积和加载时间,就像做饭时调料要适量,放多了反而会影响味道~

  • 字体的选择:不同的书籍类型适合不同的字体,比如小说适合用宋体,科技类书籍适合用黑体,就像不同的菜品需要不同的调料一样,要根据具体情况选择~


六、下一步行动

现在咱们已经学会了如何自定义字体,接下来可以试试:

  1. 实现自定义页面背景,让阅读体验更加个性化
  2. 实现字体大小、颜色的动态调整
  3. 实现多种字体的切换功能

📚 推荐资料


我是盐焗西兰花,

不教理论,只给你能跑的代码和避坑指南。

下期见!🥦

相关推荐
ly_Enhs10 分钟前
从无光照到 PBR:我的环境光照学习记录
学习·vulkan图形渲染c/c++
小李不想当小白11 分钟前
USART串口数据包(STM32标准库学习笔记)
经验分享·笔记·stm32·单片机·嵌入式硬件·学习
xier_ran20 分钟前
【infra之路】AI 编译器学习笔记(八):向量化——把小块变成 SIMD 指令
人工智能·笔记·学习
kmomo..21 分钟前
Linux 驱动开发学习笔记:I2C 子系统从入门到实践
linux·驱动开发·学习
zhangdaliangmvp1 小时前
四元数的学习理解
学习
Georgewu8 小时前
【HarmonyOS 7】 HarmonyOS 应用 Skill创建和编排详解
harmonyos
Georgewu10 小时前
【HarmonyOS 7】 应用 Agent 接入云 A2A 与端 A2A 的实践详解
harmonyos
遇印记11 小时前
传输层,网络层
运维·服务器·网络·windows·学习
clorinda12 小时前
PyQt5学习:从界面搭建到与机器学习实战
学习
欣欣之王来了14 小时前
Vue3项目脚手架:Vite vs Vue CLI怎么选?
前端·学习·架构·项目·vue3教程