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