想用自定义字体?Typeface 让你轻松管理字体文件

你有没有想过在 APP 里使用一种特别的字体?比如用一种手写体来显示标题,或者用一种等宽字体来显示代码?在 HarmonyOS 的 drawing 模块里,Typeface 类就是专门用来管理字体的。

Typeface 是什么?

下面是 Typeface 字体加载的各种方式:

系统字体目录

应用 rawfile 目录

需要加载自定义字体

字体文件位置?

makeFromFile 指定路径

makeFromRawFile 引用资源

需要指定字体属性?

使用 WithArguments 版本

直接使用基础版本

传入 TypefaceArguments 配置字重等

获得 Typeface 对象

通过 font.setTypeface 设置到 Font

用 Font 绘制文字

简单说,Typeface 就代表一种"字体"。我们平时说的"宋体"、“楷体”、“黑体”,在代码里就是不同的 Typeface 对象。每个 Typeface 对象对应一种字体设计,包含了该字体的所有字形信息。

Typeface 和 Font 的关系是:Font 是"字型",控制文字的大小、粗细、倾斜等属性;Typeface 是"字体",决定文字用哪种设计风格。你可以把 Typeface 理解为"字体文件",Font 理解为"字体设置"。

获取字体族名:getFamilyName

每个字体都有一个"族名",就是这套字体设计的名称。你可以用 getFamilyName 来获取:

import { drawing } from '@kit.ArkGraphics2D';

const font = new drawing.Font();
let typeface = font.getTypeface();
let familyName = typeface.getFamilyName();

这段代码先从 Font 对象获取关联的 Typeface,然后获取它的族名。族名通常是一个有意义的字符串,比如 “HarmonyOS Sans”、“Roboto” 等。

从字体文件加载:makeFromFile

如果你想使用系统里的某个字体文件,用 makeFromFile

import { RenderNode } from '@kit.ArkUI';
import { drawing } from '@kit.ArkGraphics2D';

class TextRenderNode extends RenderNode {
  async draw(context: DrawContext) {
    const canvas = context.canvas;
    let font = new drawing.Font();
    let str = "/system/fonts/HarmonyOS_Sans_Italic.ttf";
    const mytypeface = drawing.Typeface.makeFromFile(str);
    font.setTypeface(mytypeface);
    const textBlob = drawing.TextBlob.makeFromString("Hello World", font, drawing.TextEncoding.TEXT_ENCODING_UTF8);
    canvas.drawTextBlob(textBlob, 60, 100);
  }
}

这里我们加载了系统自带的斜体字体文件 HarmonyOS_Sans_Italic.ttf,然后用 font.setTypeface 把它设置到 Font 上。之后用这个 Font 绘制的文字就会使用斜体样式。

makeFromFile 的参数是字体文件的路径。你可以用系统字体路径(如 /system/fonts/),也可以用应用沙箱路径。

从 rawfile 加载:makeFromRawFile

如果你把字体文件打包在应用的 rawfile 目录下,可以用 makeFromRawFile 来加载:

import { RenderNode } from '@kit.ArkUI';
import { drawing } from '@kit.ArkGraphics2D';

class TextRenderNode extends RenderNode {
  async draw(context: DrawContext) {
    const canvas = context.canvas;
    let font = new drawing.Font();
    const myTypeFace = drawing.Typeface.makeFromRawFile($rawfile('HarmonyOS_Sans_Bold.ttf'));
    font.setTypeface(myTypeFace);
    const textBlob = drawing.TextBlob.makeFromString("Hello World", font, drawing.TextEncoding.TEXT_ENCODING_UTF8);
    canvas.drawTextBlob(textBlob, 60, 100);
  }
}

$rawfile('HarmonyOS_Sans_Bold.ttf') 引用的是 resources/rawfile/HarmonyOS_Sans_Bold.ttf 文件。你也可以放在子目录里,比如 $rawfile('ttf/HarmonyOS_Sans_Bold.ttf')

这种方式的好处是字体文件会随应用一起打包,不依赖系统字体。

带字体属性加载:makeFromFileWithArguments / makeFromRawFileWithArguments

从 API version 20 开始,你可以在加载字体的时候就指定字体属性:

import { RenderNode } from '@kit.ArkUI';
import { drawing } from '@kit.ArkGraphics2D';

class TextRenderNode extends RenderNode {
  async draw(context: DrawContext) {
    const canvas = context.canvas;
    let font = new drawing.Font();
    let str = "/system/fonts/HarmonyOS_Sans_Italic.ttf";
    let typeFaceArgument = new drawing.TypefaceArguments();
    const myTypeFace = drawing.Typeface.makeFromFileWithArguments(str, typeFaceArgument);
    font.setTypeface(myTypeFace);
    const textBlob = drawing.TextBlob.makeFromString("Hello World", font, drawing.TextEncoding.TEXT_ENCODING_UTF8);
    canvas.drawTextBlob(textBlob, 60, 100);
  }
}
import { RenderNode } from '@kit.ArkUI';
import { drawing } from '@kit.ArkGraphics2D';

class TextRenderNode extends RenderNode {
  async draw(context: DrawContext) {
    const canvas = context.canvas;
    let font = new drawing.Font();
    let typeFaceArgument = new drawing.TypefaceArguments();
    const myTypeFace = drawing.Typeface.makeFromRawFileWithArguments($rawfile('HarmonyOS_Sans_Bold.ttf'), typeFaceArgument);
    font.setTypeface(myTypeFace);
    const textBlob = drawing.TextBlob.makeFromString("Hello World", font, drawing.TextEncoding.TEXT_ENCODING_UTF8);
    canvas.drawTextBlob(textBlob, 60, 100);
  }
}

TypefaceArguments 可以让你指定字体的变体参数(比如字重),具体用法我们在下一篇文章里会详细讲。

基于当前字体创建变体:makeFromCurrent

如果你已经有一个 Typeface 对象,想基于它创建一个变体(比如改变字重),可以用 makeFromCurrent

import { RenderNode } from '@kit.ArkUI';
import { drawing } from '@kit.ArkGraphics2D';

class TextRenderNode extends RenderNode {
  async draw(context: DrawContext) {
    const canvas = context.canvas;
    let typeArguments = new drawing.TypefaceArguments();
    typeArguments.addVariation("wght", 100);
    const myTypeFace = drawing.Typeface.makeFromFile("/system/fonts/HarmonyOS_Sans_SC.ttf");
    const typeFace1 = myTypeFace.makeFromCurrent(typeArguments);
    let font = new drawing.Font();
    font.setTypeface(typeFace1);
    const textBlob = drawing.TextBlob.makeFromString("Hello World", font, drawing.TextEncoding.TEXT_ENCODING_UTF8);
    canvas.drawTextBlob(textBlob, 60, 100);
  }
}

这段代码先加载了 HarmonyOS_Sans_SC.ttf,然后基于它创建了一个字重为 100(非常细)的变体。TypefaceArguments 的具体用法我们在下一篇文章里会详细讲。

检查字体属性:isBold / isItalic

从 API version 23 开始,你可以检查一个 Typeface 是否是粗体或斜体:

import { drawing } from '@kit.ArkGraphics2D';

const font = new drawing.Font();
let typeface = font.getTypeface();
let isBold = typeface.isBold();
let isItalic = typeface.isItalic();

这两个方法在需要根据字体样式做不同处理时很有用。比如你可能想根据当前字体是否是粗体来决定是否需要额外加粗。

实际应用场景

下面是多语言字体选择的典型流程:

包含中文字符

纯英文字符

需要等宽字体

需要绘制文字

检测文字语言

使用中文 Typeface

使用英文 Typeface

使用 monospace Typeface

设置到 Font 对象

设置字号和其他属性

创建 TextBlob

调用 canvas.drawTextBlob 绘制

场景一:使用自定义品牌字体

很多 APP 都有自己的品牌字体。你可以把字体文件放在 rawfile 目录下,然后用 Typeface 加载:

let brandFont = drawing.Typeface.makeFromRawFile($rawfile('MyBrandFont.ttf'));
let font = new drawing.Font();
font.setTypeface(brandFont);
font.setSize(24);
// 用这个 font 绘制品牌文字

场景二:代码高亮显示

做代码编辑器或代码显示功能时,通常需要等宽字体:

let codeFont = drawing.Typeface.makeFromFile("/system/fonts/monospace.ttf");
let font = new drawing.Font();
font.setTypeface(codeFont);
font.setSize(14);
// 用这个 font 显示代码

场景三:多语言支持

不同语言可能需要不同的字体。比如中文用一种字体,英文用另一种:

let chineseFont = drawing.Typeface.makeFromFile("/system/fonts/HarmonyOS_Sans_SC.ttf");
let englishFont = drawing.Typeface.makeFromFile("/system/fonts/HarmonyOS_Sans.ttf");

// 根据文本内容选择字体
function getFontForText(text: string): drawing.Font {
  let font = new drawing.Font();
  if (/[\u4e00-\u9fa5]/.test(text)) {
    font.setTypeface(chineseFont);
  } else {
    font.setTypeface(englishFont);
  }
  font.setSize(20);
  return font;
}

场景四:字体变体切换

同一个字体族可能有多种变体(细体、常规、粗体等),用 Typeface 可以方便地切换:

let regularTypeface = drawing.Typeface.makeFromFile("/system/fonts/HarmonyOS_Sans_Regular.ttf");
let boldTypeface = drawing.Typeface.makeFromFile("/system/fonts/HarmonyOS_Sans_Bold.ttf");
let italicTypeface = drawing.Typeface.makeFromFile("/system/fonts/HarmonyOS_Sans_Italic.ttf");

// 根据需要选择不同的变体

注意事项

  1. API 版本:Typeface 基础方法从 API version 11 开始支持,makeFromFile 从 12 开始,makeFromRawFile 从 18 开始,带 Arguments 的方法从 20 开始,isBold/isItalic 从 23 开始。

  2. 物理像素:和其他 drawing 对象一样,使用物理像素单位。

  3. 线程安全:单线程模型,需要自己管理线程安全。

  4. 字体文件路径:确保字体文件路径正确,文件存在且可读。如果文件不存在,makeFromFile 可能会返回空指针。

  5. 性能考虑:加载字体文件是一个相对耗时的操作,建议把常用的 Typeface 对象缓存起来重复使用,而不是每次需要时都重新加载。

小结

Typeface 是 drawing 模块里管理字体的核心类。它让你能够:

  • 从系统字体或 rawfile 加载自定义字体
  • 获取字体的族名
  • 基于现有字体创建变体
  • 检查字体是否是粗体或斜体

在实际开发中,Typeface 通常和 Font 配合使用——Typeface 决定"用什么字体",Font 决定"怎么显示"。掌握了 Typeface,你就能在 APP 里使用任何你喜欢的字体了。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐