1# 使用主题字体(ArkTS) 2<!--Kit: ArkGraphics 2D--> 3<!--Subsystem: Graphics--> 4<!--Owner: @oh_wangxk; @gmiao522; @Lem0nC--> 5<!--Designer: @liumingxiang--> 6<!--Tester: @yhl0101--> 7<!--Adviser: @ge-yafang--> 8## 场景介绍 9 10主题字体,特指系统**主题应用**中能使用的字体,属于一种特殊的自定义字体,可以通过相关接口调用使能主题应用中的主题字体。 11 12## 实现机制 13 14**图1** 主题字体的切换和使用 15 16 17 18针对主题字的切换使用,应用方应确保订阅主题字变更事件,当接收字体变更事件后,由应用方主动调用页面刷新才能实现主题字的切换,否则主题字只能在重启应用后才生效。 19 20 21## 接口说明 22 23注册使用主题字体的常用接口如下表所示,详细接口说明请见[@ohos.graphics.text (文本模块)](../reference/apis-arkgraphics2d/js-apis-graphics-text.md)。 24 25| 接口 | 描述 | 26| -------- | -------- | 27| getGlobalInstance(): FontCollection | 获取应用全局字体集的实例。 | 28 29 30## 开发步骤 31 321. 请确保在设备系统**主题应用**中,能成功应用一项主题字体。 33 342. 导入依赖的相关模块。 35 36 ```ts 37 import { text } from '@kit.ArkGraphics2D'; 38 ``` 39 403. 使用getGlobalInstance()接口获取全局字体集对象,系统框架在注册主题字体过程中仅会将主题字体信息传入全局字体集对象中。 41 42 ```ts 43 let fontCollection = text.FontCollection.getGlobalInstance(); 44 ``` 45 464. 创建段落样式,并使用字体管理器实例构造段落生成器ParagraphBuilder实例,用于生成段落。 47 > **说明:** 48 > 49 > 在生成段落对象设置段落样式入参时,不能指定fontFamilies属性,否则会变为优先使用指定字体而非主题字体。 50 > 51 > 若未在系统**主题应用**中设置一项主题字体,则将使用系统默认字体进行绘制。 52 53 ```ts 54 // 设置文本样式 55 let myTextStyle: text.TextStyle = { 56 color: { alpha: 255, red: 255, green: 0, blue: 0 }, 57 fontSize: 100, 58 // fontFamilies:['Test Font'] // 不要指定fontFamilies,否则优先使用指定字体 59 }; 60 // 创建一个段落样式对象,以设置排版风格 61 let myParagraphStyle: text.ParagraphStyle = {textStyle: myTextStyle} 62 // 创建一个段落生成器 63 let paragraphBuilder: text.ParagraphBuilder = new text.ParagraphBuilder(myParagraphStyle, fontCollection); 64 ``` 65 665. 设置文本样式,添加文本内容,并生成段落文本用于后续文本的绘制显示。 67 68 ```ts 69 // 在段落生成器中设置文本样式 70 paragraphBuilder.pushStyle(myTextStyle); 71 // 在段落生成器中设置文本内容 72 paragraphBuilder.addText("Hello World. \nThis is the theme font."); 73 // 通过段落生成器生成段落 74 let paragraph = paragraphBuilder.build(); 75 ``` 76 776. 创建渲染节点,并保存到数组。(此处示例代码为简化逻辑,采用数组作为容器,实际开发中应结合应用情况选择更恰当的容器来保证节点的添加与删除对应。) 78 79 ```ts 80 // 创建渲染节点数组 81 const renderNodeMap: Array<RenderNode> = new Array(); 82 // 创建节点控制器 83 class MyNodeController extends NodeController { 84 private rootNode: FrameNode | null = null; 85 makeNode(uiContext: UIContext): FrameNode { 86 this.rootNode = new FrameNode(uiContext) 87 if (this.rootNode == null) { 88 return this.rootNode 89 } 90 const renderNode = this.rootNode.getRenderNode() 91 if (renderNode != null) { 92 renderNode.frame = { x: 0, y: 0, width: 300, height: 50 } 93 renderNode.pivot = { x: 0, y: 0 } 94 } 95 return this.rootNode 96 } 97 addNode(node: RenderNode): void { 98 if (this.rootNode == null) { 99 return 100 } 101 const renderNode = this.rootNode.getRenderNode() 102 if (renderNode != null) { 103 renderNode.appendChild(node) 104 // 将节点添加到渲染节点数组中 105 renderNodeMap.push(node) 106 } 107 } 108 clearNodes(): void { 109 if (this.rootNode == null) { 110 return 111 } 112 const renderNode = this.rootNode.getRenderNode() 113 if (renderNode != null) { 114 renderNode.clearChildren() 115 // 将节点从渲染节点数组中移除 116 renderNodeMap.pop() 117 } 118 } 119 } 120 let paragraph = paragraphBuilder.build(); 121 ``` 122 1237. 创建渲染节点更新函数,并导出函数,供其他文件(如:EntryAbility.ets)使用;重绘制节点目的为更新排版中字体信息,若不更新字体信息,使用之前残留结果,可能造成文字乱码。 124 125 ```ts 126 // 导出渲染节点更新函数 127 export function updateRenderNodeData() { 128 renderNodeMap.forEach((node) => { 129 // 主动触发节点重绘制 130 node.invalidate() 131 }) 132 } 133 ``` 134 1358. 在EntryAbility.ets中接收主题字变更事件,并调用渲染节点更新函数。 136 137 ```ts 138 // entry/src/main/ets/entryability/EntryAbility.ets 139 export default class EntryAbility extends UIAbility { 140 // ... 141 preFontId =""; 142 onConfigurationUpdate(newConfig: Configuration):void{ 143 let fontId = newConfig.fontId; 144 if(fontId && fontId !=this.preFontId){ 145 this.preFontId = fontId; 146 updateRenderNodeData(); 147 } 148 } 149 // ... 150 } 151 ``` 152 153 154## 完整示例 155 156这里以使用主题字体绘制"Hello World. \nThis is the theme font."文本为例,提供完整的示例和效果示意图。 157 158```ts 159// /pages/Index.ets 160import { NodeController, FrameNode, RenderNode, DrawContext } from '@kit.ArkUI'; 161import { UIContext } from '@kit.ArkUI'; 162import { text } from '@kit.ArkGraphics2D'; 163 164// 创建一个自定义的渲染节点类,用于绘制文本 165class MyRenderNode extends RenderNode { 166 async draw(context: DrawContext) { 167 // 获取画布canvas对象 168 const canvas = context.canvas; 169 // 设置文本样式 170 let myTextStyle: text.TextStyle = { 171 color: { alpha: 255, red: 255, green: 0, blue: 0 }, 172 fontSize: 33 173 }; 174 // 创建一个段落样式对象,以设置排版风格 175 let myParagraphStyle: text.ParagraphStyle = { 176 textStyle: myTextStyle, 177 align: 3, 178 wordBreak:text.WordBreak.NORMAL 179 }; 180 // 获取字体管理器全局FontCollection实例 181 let fontCollection = text.FontCollection.getGlobalInstance(); // 获取Arkui全局FC 182 // 创建一个段落生成器 183 let paragraphGraphBuilder = new text.ParagraphBuilder(myParagraphStyle, fontCollection); 184 // 在段落生成器中设置文本样式 185 paragraphGraphBuilder.pushStyle(myTextStyle); 186 // 在段落生成器中设置文本内容 187 paragraphGraphBuilder.addText("Hello World. \nThis is the theme font."); 188 // 通过段落生成器生成段落 189 let paragraph = paragraphGraphBuilder.build(); 190 // 布局 191 paragraph.layoutSync(2500); 192 paragraph.paint(canvas, 0, 400); 193 } 194} 195// 创建渲染节点数组 196const renderNodeMap: Array<RenderNode> = new Array(); 197// 导出渲染节点更新函数 198export function updateRenderNodeData() { 199 renderNodeMap.forEach((node) => { 200 // 主动触发节点重绘制 201 node.invalidate(); 202 }); 203} 204 205class MyNodeController extends NodeController { 206 private rootNode: FrameNode | null = null; 207 makeNode(uiContext: UIContext): FrameNode { 208 this.rootNode = new FrameNode(uiContext); 209 if (this.rootNode == null) { 210 return this.rootNode; 211 } 212 const renderNode = this.rootNode.getRenderNode(); 213 if (renderNode != null) { 214 renderNode.frame = { x: 0, y: 0, width: 300, height: 50 }; 215 renderNode.pivot = { x: 0, y: 0 }; 216 } 217 return this.rootNode; 218 } 219 addNode(node: RenderNode): void { 220 if (this.rootNode == null) { 221 return; 222 } 223 const renderNode = this.rootNode.getRenderNode(); 224 if (renderNode != null) { 225 renderNode.appendChild(node); 226 // 将节点添加到渲染节点数组中 227 renderNodeMap.push(node); 228 } 229 } 230 clearNodes(): void { 231 if (this.rootNode == null) { 232 return; 233 } 234 const renderNode = this.rootNode.getRenderNode(); 235 if (renderNode != null) { 236 renderNode.clearChildren(); 237 // 将节点从渲染节点数组中移除 238 renderNodeMap.pop(); 239 } 240 } 241} 242 243// 创建一个TextRenderNode对象 244const textNode = new MyRenderNode(); 245// 定义textNode的像素格式 246textNode.frame = { 247 x: 0, 248 y: 100, 249 width: 600, 250 height: 800 251}; 252textNode.pivot = { x: 0.2, y: 0.8 }; 253textNode.scale = { x: 1, y: 1 }; 254 255@Entry 256@Component 257struct RenderTest { 258 private myNodeController: MyNodeController = new MyNodeController(); 259 build() { 260 Column() { 261 Row() { 262 NodeContainer(this.myNodeController) 263 .height('100%') 264 } 265 .height('90%') 266 .backgroundColor(Color.White) 267 Row(){ 268 Button("Draw Text") 269 .fontSize('16fp') 270 .fontWeight(500) 271 .margin({ bottom: 24, right: 12 }) 272 .onClick(() => { 273 this.myNodeController.clearNodes(); 274 this.myNodeController.addNode(textNode); 275 }) 276 .width('50%') 277 .height(40) 278 .shadow(ShadowStyle.OUTER_DEFAULT_LG) 279 } 280 .width('100%') 281 .justifyContent(FlexAlign.Center) // 设置当前Row容器内子元素在主轴上居中对齐 282 .shadow(ShadowStyle.OUTER_DEFAULT_SM) // 设置Row容器外阴影效果 283 .alignItems(VerticalAlign.Bottom) // 设置当前Row容器内子元素在交叉轴(垂直方向)上的对齐方式为底部对齐 284 .layoutWeight(1) // 设置当前Row在父容器Column中的布局权重为1 285 } 286 } 287} 288 289 290``` 291 292```ts 293// entry/src/main/ets/entryability/EntryAbility.ets 294import { AbilityConstant, Configuration, UIAbility, Want } from '@kit.AbilityKit'; 295import { hilog } from '@kit.PerformanceAnalysisKit'; 296import { window } from '@kit.ArkUI'; 297import { updateRenderNodeData } from '../pages/Index'; 298export default class EntryAbility extends UIAbility { 299 // ... 300 preFontId =""; 301 onConfigurationUpdate(newConfig: Configuration):void{ 302 let fontId = newConfig.fontId; 303 if(fontId && fontId !=this.preFontId){ 304 this.preFontId = fontId; 305 updateRenderNodeData(); 306 } 307 } 308 // ... 309} 310``` 311 312## 效果展示 313 314以下展示了在系统**主题应用**中切换使用不同主题字体后,对应的文字渲染效果。 315 316不同主题字体显示效果不同,此处仅示意。 317 318**图2** 主题字体1的效果 319 320 321 322**图3** 主题字体2的效果 323 324 325