• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 使用主题字体(ArkTS)
2
3## 场景介绍
4
5主题字体,特指系统**主题应用**中能使用的字体,属于一种特殊的自定义字体,可以通过相关接口调用使能主题应用中的主题字体。
6
7## 实现机制
8
9**图1** 主题字体的切换和使用
10
11![themeText_native](figures/themeText_native.jpg)
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![themeFont_ts_01](figures/themeFont_ts_01.png)
312
313**图3** 主题字体2的效果
314
315![themeFont_ts_02](figures/themeFont_ts_02.png)
316