ArKTS: font
/**
# encoding: utf-8
# 版权所有 2025 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:
# Author : geovindu,Geovin Du 涂聚文.
# IDE : DevEco Studio 5.1.1 HarmonyOS ArKTS
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2025/8/9 5:22
# User : geovindu
# Product : DevEco Studio
# Project : sqliteAppHelper
# File : DuNodeController.ets
**/
import { NodeController, FrameNode, RenderNode, DrawContext } from '@kit.ArkUI'
import { UIContext } from '@kit.ArkUI'
import { text } from '@kit.ArkGraphics2D'
/**
* 自定义节点控制器类
*/
export class DuNodeController extends NodeController {
private rootNode: FrameNode | null = null;
makeNode(uiContext: UIContext): FrameNode {
this.rootNode = new FrameNode(uiContext)
if (this.rootNode == null) {
return this.rootNode
}
const renderNode = this.rootNode.getRenderNode()
if (renderNode != null) {
renderNode.frame = { x: 0, y: 0, width: 300, height: 50 }
renderNode.pivot = { x: 0, y: 0 }
}
return this.rootNode
}
addNode(node: RenderNode): void {
if (this.rootNode == null) {
return
}
const renderNode = this.rootNode.getRenderNode()
if (renderNode != null) {
renderNode.appendChild(node)
console.log('节点已添加到控制器');
}
}
clearNodes(): void {
if (this.rootNode == null) {
return
}
const renderNode = this.rootNode.getRenderNode()
if (renderNode != null) {
renderNode.clearChildren()
}
}
}
/**
# encoding: utf-8
# 版权所有 2025 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:
# Author : geovindu,Geovin Du 涂聚文.
# IDE : DevEco Studio 5.1.1 HarmonyOS ArKTS
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2025/8/9 5:19
# User : geovindu ArkGraphics2D
# Product : DevEco Studio
# Project : sqliteAppHelper
# File : DuRenderNode.ets
**/
import { NodeController, FrameNode, RenderNode, DrawContext } from '@kit.ArkUI'
import { UIContext } from '@kit.ArkUI'
import { text } from '@kit.ArkGraphics2D'
/**
* 创建一个自定义的渲染节点类,用于绘制文本
*/
export class DuRenderNode extends RenderNode {
async draw(context: DrawContext) {
// 创建画布canvas对象
const canvas = context.canvas
// 获取全局字体集实例
let fontCollection = text.FontCollection.getGlobalInstance() //获取Arkui全局FC
// 注册自定义字体
fontCollection.loadFontSync('myFamilyName', 'file:///system/fonts/NotoSansMalayalamUI-SemiBold.ttf')
// 方式二:确保已经将自定义字体myFontFile.ttf文件放在本应用工程的entry/src/main/resources/rawfile目录
fontCollection.loadFontSync('mao',$rawfile('mao.ttf'))
// 使用自定义字体
let myFontFamily: Array<string> = ["mao"] // 如果已经注册自定义字体,填入自定义字体的字体家族名
// 设置文本样式
let myTextStyle: text.TextStyle = {
color: { alpha: 255, red: 255, green: 0, blue: 0 },
fontSize: 80,
// 在文本样式中加入可使用的自定义字体
fontFamilies: myFontFamily
};
// 创建一个段落样式对象,以设置排版风格
let myParagraphStyle: text.ParagraphStyle = {
textStyle: myTextStyle,
align: 3,
wordBreak:text.WordBreak.NORMAL
};
// 创建一个段落生成器
let ParagraphGraphBuilder = new text.ParagraphBuilder(myParagraphStyle, fontCollection)
// 在段落生成器中设置文本样式
ParagraphGraphBuilder.pushStyle(myTextStyle);
// 在段落生成器中设置文本内容
ParagraphGraphBuilder.addText("《沁园春·雪》\n" +
"\n" +
"作者:毛\n北国风光,千里冰封,万里雪飘。\n" +
"\n" +
"望长城内外,惟余莽莽;大河上下,顿失滔滔。\n" +
"\n" +
"山舞银蛇,原驰蜡象,欲与天公试比高。\n" +
"\n" +
"须晴日,看红装素裹,分外妖娆。\n" +
"\n" +
"江山如此多娇,引无数英雄竞折腰。\n" +
"\n" +
"惜秦皇汉武,略输文采;唐宗宋祖,稍逊风骚。\n" +
"\n" +
"一代天骄,成吉思汗,只识弯弓射大雕。\n" +
"\n" +
"俱往矣,数风流人物,还看今朝。");
// 通过段落生成器生成段落
let paragraph = ParagraphGraphBuilder.build();
// 设置段落宽度为1000px
paragraph.layoutSync(1000);
paragraph.paint(canvas, 0, 400);
}
}
/**
# encoding: utf-8
# 版权所有 2025 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述: 自定义字体页面
# Author : geovindu,Geovin Du 涂聚文.
# IDE : DevEco Studio 5.1.1 HarmonyOS ArKTS
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2025/8/9 5:19
# User : geovindu
# Product : DevEco Studio
# Project : sqliteAppHelper
# File : CustomFontPage.ets
**/
import { NodeController, FrameNode, RenderNode, DrawContext } from '@kit.ArkUI'
import { UIContext } from '@kit.ArkUI'
import { text } from '@kit.ArkGraphics2D'
import { DuNodeController } from "../utils/DuNodeController"
import { DuRenderNode } from "../utils/DuRenderNode"
// 创建并初始化渲染节点实例
const newNode = new DuRenderNode();
// 设置渲染节点的位置和尺寸
newNode.frame = { x: 0, y: 0, width: 400, height: 600 };
@Entry
@Component
struct MaoFontntPage {
@State message: string = 'Hello Geovin Dui';
private Controller: DuNodeController = new DuNodeController()
build() {
Scroll()
{
Column() {
Row() {
NodeContainer(this.Controller)
.height('100%')
}
.height('99%')
.backgroundColor(Color.White)
Row(){
Button("写字")
.fontSize('16fp')
.fontWeight(500)
.margin({ bottom: 24, right: 12 })
.onClick(() => {
this.Controller.clearNodes()
this.Controller.addNode(newNode)
})
.width('50%')
.height(40)
.shadow(ShadowStyle.OUTER_DEFAULT_LG)
}
.width('100%')
.justifyContent(FlexAlign.Center) // 设置当前Row容器内子元素在主轴上居中对齐
.shadow(ShadowStyle.OUTER_DEFAULT_SM) // 设置Row容器外阴影效果
.alignItems(VerticalAlign.Bottom) // 设置当前Row容器内子元素在交叉轴(垂直方向)上的对齐方式为底部对齐
.layoutWeight(1) // 设置当前Row在父容器Column中的布局权重为1
}
}
}
}
/**
# encoding: utf-8
# 版权所有 2025 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述: 自定义字体页面
# Author : geovindu,Geovin Du 涂聚文.
# IDE : DevEco Studio 5.1.1 HarmonyOS ArKTS
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2025/8/9 5:19
# User : geovindu
# Product : DevEco Studio
# Project : sqliteAppHelper
# File : CustomFontPage.ets
**/
import { NodeController, FrameNode, RenderNode, DrawContext } from '@kit.ArkUI'
import { UIContext } from '@kit.ArkUI'
import { text } from '@kit.ArkGraphics2D'
import { DuNodeController } from "../utils/DuNodeController"
import { DuRenderNode } from "../utils/DuRenderNode"
import font from '@ohos.font';
// 创建并初始化渲染节点实例
const newNode = new DuRenderNode();
// 设置渲染节点的位置和尺寸
newNode.frame = { x: 0, y: 0, width: 400, height: 600 };
@Entry
@Component
struct MaoFontntPage {
@State message: string = 'Hello Geovin Dui';
aboutToAppear() {
// familyName和familySrc都支持string
font.registerFont({
familyName: 'maozedong',
familySrc: 'font/maozedong.ttf',// font文件与pages目录同级
})
}
private Controller: DuNodeController = new DuNodeController()
build() {
Scroll()
{
Column() {
Row() {
NodeContainer(this.Controller)
.height('100%')
}
.height('99%')
.backgroundColor(Color.White)
Row(){
Button("写字")
.fontSize('16fp')
.fontWeight(500)
.fontFamily('maozedong')
.margin({ bottom: 24, right: 12 })
.onClick(() => {
this.Controller.clearNodes()
this.Controller.addNode(newNode)
})
.width('50%')
.height(40)
.shadow(ShadowStyle.OUTER_DEFAULT_LG)
}
.width('100%')
.justifyContent(FlexAlign.Center) // 设置当前Row容器内子元素在主轴上居中对齐
.shadow(ShadowStyle.OUTER_DEFAULT_SM) // 设置Row容器外阴影效果
.alignItems(VerticalAlign.Bottom) // 设置当前Row容器内子元素在交叉轴(垂直方向)上的对齐方式为底部对齐
.layoutWeight(1) // 设置当前Row在父容器Column中的布局权重为1
}
}
}
}
项目结构:



/**
# encoding: utf-8
# 版权所有 2025 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述: 自定义字体页面 https://developer.huawei.com/consumer/en/doc/harmonyos-references-V14/ts-canvasrenderingcontext2d-V14#font
# Author : geovindu,Geovin Du 涂聚文.
# IDE : DevEco Studio 5.1.1 HarmonyOS ArKTS
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2025/8/9 5:19
# User : geovindu
# Product : DevEco Studio
# Project : sqliteAppHelper
# File : CustomFontPage.ets
**/
import promptAction from '@ohos.promptAction'
import common from '@ohos.app.ability.common';
import { BusinessError } from '@ohos.base';
import { text } from '@kit.ArkGraphics2D';
import { AddFormMenuItem } from '@kit.ArkUI';
import { formBindingData } from '@kit.FormKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import font from '@ohos.font';
class TextTyper {
private fullText: string = "《沁园春·雪》\n\n作者:毛\n北国风光,千里冰封,万里雪飘。\n\n望长城内外,惟余莽莽;大河上下,顿失滔滔。\n\n山舞银蛇,原驰蜡象,欲与天公试比高。\n\n须晴日,看红装素裹,分外妖娆。\n\n江山如此多娇,引无数英雄竞折腰。\n\n惜秦皇汉武,略输文采;唐宗宋祖,稍逊风骚。\n\n一代天骄,成吉思汗,只识弯弓射大雕。\n\n俱往矣,数风流人物,还看今朝。";
currentText: string = "";
currentIndex: number = 0;
typingSpeed: number = 100;
timerId: number | null = null;
fontCollection: text.FontCollection;
fontLoaded: boolean = false;
fontName: string = 'maozedong';
fontPath: string = 'maozedong.ttf';
// 新增:字体文件信息校验
fontFileSize: number = 0; // 用于验证文件是否有效
constructor() {
this.fontCollection = text.FontCollection.getGlobalInstance();
this.checkFontFileExists(); // 先检查文件是否可访问
this.loadCustomFont();
}
// 关键:检查字体文件是否真的可访问
private async checkFontFileExists() {
try {
const context = getContext(this) as common.UIAbilityContext;
// 尝试读取文件元数据判断是否存在
const fileInfo = context.resourceManager.getRawFile(this.fontPath);
console.log(`[文件检查] 存在: ${this.fontPath}, 大小: ${this.fontFileSize}字节`);
if (this.fontFileSize < 1024) { // 小于1KB的字体文件几乎肯定无效
promptAction.showToast({ message: `警告:字体文件过小,可能损坏` });
}
} catch (error) {
const err = error as BusinessError;
console.error(`[文件检查] 不存在或无法访问: ${this.fontPath}, 错误: ${err.code}`);
promptAction.showToast({ message: `文件访问失败: ${err.code}` });
}
}
// 优化的字体加载逻辑,增加格式兼容性处理
private async loadCustomFont() {
// 先确保文件存在且有效
if (this.fontFileSize === 0) {
console.log(`[字体加载] 跳过加载,文件无效`);
return;
}
try {
console.log(`[字体加载] 开始加载有效文件 (${this.fontFileSize}字节)`);
// 尝试不同的字体加载方式
const loadResult = await this.fontCollection.loadFont(
this.fontName,
$rawfile(this.fontPath),
).then(
() => {
console.log(`[字体加载] 加载成功`);
return true;
},
(error:BusinessError) => {
console.error(`[字体加载] 加载失败: ${error}`);
return false;
}
);
this.fontLoaded = loadResult;
console.log(`[字体加载] 加载结果: ${this.fontLoaded}`);
// 加载成功但可能未生效的额外提示
if (this.fontLoaded) {
promptAction.showToast({
message: `字体加载成功,若显示异常请尝试更换TTF文件`
});
} else {
promptAction.showToast({ message: `字体格式不兼容,请使用标准TTF` });
}
} catch (error) {
this.fontLoaded = false;
const err = error as BusinessError;
console.error(`[字体加载] 异常: ${err.code}-${err.message}`);
// 常见错误码提示
const errorTips: Record<number, string> = {
13900002: "文件格式错误(非TTF)",
13900003: "文件损坏或加密",
13900004: "不支持的字体格式"
};
const tip = errorTips[err.code] || `错误码: ${err.code}`;
promptAction.showToast({ message: `加载失败: ${tip}` });
}
}
startTyping(invalidate: () => void) {
this.currentText = "";
this.currentIndex = 0;
if (this.timerId) clearInterval(this.timerId);
this.timerId = setInterval(() => {
if (this.currentIndex < this.fullText.length) {
this.currentText += this.fullText[this.currentIndex];
this.currentIndex++;
invalidate();
} else {
clearInterval(this.timerId!);
this.timerId = null;
}
}, this.typingSpeed);
}
resetTyping() {
if (this.timerId) clearInterval(this.timerId);
this.currentText = "";
this.currentIndex = 0;
}
}
@Entry
@Component
struct CustomFontPage {
private settings = new RenderingContextSettings(true);
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
@State needRedraw: boolean = false;
private typer = new TextTyper();
private canvasWidth: number = 600;
private canvasHeight: number = 800;
private fontColor: string = '#8B0000';
private fontSize: number = 46;
private lineHeight: number = 20;
// 测试文本,用于直观对比字体效果
private testText: string = "测试文本:毛字体";
private draw() {
if (!this.context) return;
// 清空画布
this.context.fillStyle = '#ffffff';
this.context.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
// 构建字体样式SimHei
const targetFont = this.typer.fontLoaded
? `${this.typer.fontName}`
: `${this.typer.fontName}`;
const fontStyle = `${this.fontSize}px ${this.typer.fontName}`; //, Microsoft YaHei, sans-serif
this.context.font = fontStyle;
// 字体生效检测
this.context.fillStyle = '#666666';
this.context.fillText(`当前字体配置: ${targetFont}`, 10, 40);
// 显示测试文本对比
this.context.fillStyle = '#0000ff';
this.context.fillText(this.testText, 10, 80);
this.context.font = `${this.fontSize}px ${this.typer.fontName}`; // 系统字体对比
this.context.fillStyle = '#008000';
this.context.fillText(`系统字体对比: ${this.testText}`, 10, 120);
// 恢复设置绘制主文本
this.context.font = fontStyle;
this.context.fillStyle = this.fontColor;
this.drawCenteredText(this.typer.currentText);
}
// 显示字体文件状态信息
private showFontFileInfo() {
return ""
}
private calculateTextHeight(text: string): number {
const lines = text.split('\n');
return lines.length * this.lineHeight;
}
private drawCenteredText(text: string) {
const lines = text.split('\n');
const totalHeight = this.calculateTextHeight(text);
let lineY = (this.canvasHeight - totalHeight) / 2 + this.fontSize;
lines.forEach(line => {
if (line) {
const textWidth = this.context.measureText(line).width;
const x = (this.canvasWidth - textWidth) / 2;
this.context.fillText(line, x, lineY);
this.context.font = `${this.fontSize}px ${this.typer.fontName}`;
// this.context.font= `${this.fontSize}vp maozedong`
}
lineY += this.lineHeight;
});
}
aboutToAppear() {
// familyName和familySrc都支持string
font.registerFont({
familyName: 'maozedong',
familySrc: 'font/maozedong.ttf' // font文件与pages目录同级
})
}
build() {
Column({space: 10}) {
// 显示字体文件信息,帮助调试
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Canvas(this.context)
.width(this.canvasWidth)
.height(this.canvasHeight)
.backgroundColor(Color.White)
.onReady(() => this.draw())
Row() {
Button("开始写字")
.fontFamily('maozedong')
.fontSize('16fp')
.margin({ right: 12 })
.onClick(() => {
this.typer.startTyping(() => {
this.needRedraw = !this.needRedraw;
this.draw();
});
})
.width('45%')
.height(40)
Button("重置")
.fontSize('16fp')
.fontFamily('maozedong')
.margin({ left: 12 })
.onClick(() => {
this.typer.resetTyping();
this.needRedraw = !this.needRedraw;
this.draw();
})
.width('45%')
.height(40)
}
.margin(12)
}
.width('99%')
.height('100%')
}
.width('100%')
.height('100%')
.backgroundColor(Color.White)
.padding(5)
}
}
卡片:
https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-container-formlink-V5
ohpm install @mcui/mccharts
https://gitee.com/openharmony-sig/ohos_axios
https://developer.huawei.com/consumer/cn/doc/harmonyos-references/canvas-drawing
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/custom-font-arkts 自定义字体的注册和使用
https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkui-js-full-comp 兼容JS的类Web开发范式(ArkUI.Full)
https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-arkui-21 如何加载和使用自定义字体 方舟UI框架(ArkUI)
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ui-js-dev
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/1_3arkts_u8bed_u8a00_u57fa_u7840_u7c7b_u5e93-0000001632530086-V2 ArkTS语言基础类库
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/system-font-arkts
系统字体的信息获取和使用(ArkTS)
浙公网安备 33010602011771号