• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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- 多行文本绘制与显示
15
16- 多样式文本绘制与显示
17
18
19## 多语言文本绘制与显示
20
21多语言支持是全球化应用的基础。多语言文本绘制需要支持不同语言的字符集及其独特的显示需求,例如右到左语言(如阿拉伯语)或竖排文本(如中文)。开发者需要理解不同语言的渲染特性,确保文本的正确显示。
22
23
24在多语言文本使用的场景下,主要通过指定[TextStyle](../reference/apis-arkgraphics2d/js-apis-graphics-text.md#textstyle)文本样式中的**locale**字段来实现,可直接通过locale字段的值优先匹配对应字体,跳过遍历列表匹配字体的过程,从而降低匹配时间和内存使用。
25
26
27### 开发步骤
28
291. 通过context获取到Canvas画布对象。
30
31   ```ts
32   let canvas = context.canvas;
33   ```
34
352. 初始化文本样式。
36
37   ```ts
38   let myTextStyle: text.TextStyle = {
39     color: {
40       alpha: 255,
41       red: 255,
42       green: 0,
43       blue: 0
44     },
45     fontSize: 50,
46     // 设置语言偏好为简体中文
47     locale: "zh-Hans"
48   };
49   ```
50
513. 初始化段落样式。
52
53   ```ts
54   let myParagraphStyle: text.ParagraphStyle = {
55     textStyle: myTextStyle,
56   };
57   ```
58
594. 初始化段落对象,并添加文本。
60
61   ```ts
62   let fontCollection = text.FontCollection.getGlobalInstance();
63   let paragraphBuilder = new text.ParagraphBuilder(myParagraphStyle, fontCollection);
64   // 更新文本样式
65   paragraphBuilder.pushStyle(myTextStyle);
66   // 添加文本
67   paragraphBuilder.addText("你好,世界");
68   ```
69
705. 排版段落并进行文本绘制。
71
72   ```ts
73   // 生成段落
74   let paragraph = paragraphBuilder.build();
75   // 布局
76   paragraph.layoutSync(1250);
77   // 绘制文本
78   paragraph.paint(canvas, 10, 0);
79   ```
80
81
82### 完整示例
83
84此示例中,要绘制的文本为简体中文,将语言偏好设置为简体中文,在匹配文字字体时,会优先匹配简体,从而提高绘制的效率。
85
86```ts
87import { NodeController, FrameNode, RenderNode, DrawContext } from '@kit.ArkUI'
88import { UIContext } from '@kit.ArkUI'
89import { drawing } from '@kit.ArkGraphics2D'
90import { text } from '@kit.ArkGraphics2D'
91import { image } from '@kit.ImageKit'
92import { common2D } from '@kit.ArkGraphics2D'
93
94// 创建一个MyRenderNode类,并绘制文本。
95class MyRenderNode extends RenderNode {
96  async draw(context: DrawContext) {
97    // 绘制代码逻辑写在这里
98    let canvas = context.canvas;
99
100    let myTextStyle: text.TextStyle = {
101      color: {
102        alpha: 255,
103        red: 255,
104        green: 0,
105        blue: 0
106      },
107      fontSize: 50,
108      // 设置语言偏好为简体中文
109      locale: "zh-Hans"
110    };
111
112    let myParagraphStyle: text.ParagraphStyle = {
113      textStyle: myTextStyle,
114    };
115    let fontCollection = text.FontCollection.getGlobalInstance();
116    let paragraphBuilder = new text.ParagraphBuilder(myParagraphStyle, fontCollection);
117    // 更新文本样式
118    paragraphBuilder.pushStyle(myTextStyle);
119    // 添加文本
120    paragraphBuilder.addText("你好,世界");
121    // 生成段落
122    let paragraph = paragraphBuilder.build();
123    // 布局
124    paragraph.layoutSync(1250);
125    // 绘制文本
126    paragraph.paint(canvas, 10, 0);
127  }
128}
129
130// 创建一个MyRenderNode对象
131const textNode = new MyRenderNode()
132// 定义newNode的像素格式
133textNode.frame = {
134  x: 0,
135  y: 0,
136  width: 400,
137  height: 600
138}
139textNode.pivot = { x: 0.2, y: 0.8 }
140textNode.scale = { x: 1, y: 1 }
141
142class MyNodeController extends NodeController {
143  private rootNode: FrameNode | null = null;
144
145  makeNode(uiContext: UIContext): FrameNode {
146    this.rootNode = new FrameNode(uiContext)
147    if (this.rootNode == null) {
148      return this.rootNode
149    }
150    const renderNode = this.rootNode.getRenderNode()
151    if (renderNode != null) {
152      renderNode.frame = {
153        x: 0,
154        y: 0,
155        width: 10,
156        height: 500
157      }
158    }
159    return this.rootNode
160  }
161
162  addNode(node: RenderNode): void {
163    if (this.rootNode == null) {
164      return
165    }
166    const renderNode = this.rootNode.getRenderNode()
167    if (renderNode != null) {
168      renderNode.appendChild(node)
169    }
170  }
171
172  clearNodes(): void {
173    if (this.rootNode == null) {
174      return
175    }
176    const renderNode = this.rootNode.getRenderNode()
177    if (renderNode != null) {
178      renderNode.clearChildren()
179    }
180  }
181}
182
183let myNodeController: MyNodeController = new MyNodeController()
184
185async function performTask() {
186  myNodeController.clearNodes()
187  myNodeController.addNode(textNode)
188}
189
190@Entry
191@Component
192struct Font08 {
193  @State src: Resource = $r('app.media.startIcon')
194  build() {
195    Column() {
196      Row() {
197        NodeContainer(myNodeController)
198          .height('100%')
199          .width('100%')
200        Image(this.src)
201          .width('0%').height('0%')
202          .onComplete(
203            () => {
204              performTask();
205            })
206      }
207      .width('100%')
208    }
209  }
210}
211```
212
213
214### 效果展示
215
216![zh-cn_image_0000002246603733](figures/zh-cn_image_0000002246603733.png)
217
218
219## 多行文本绘制与显示
220
221多行文本相对于单行文本比较复杂,一般针对多行文本,需要进行文本排版、断词策略设置、文本对齐方式、最大行数限制等,主要通过设置段落样式实现。
222
223
224### 实现说明
225
226**段落样式**([ParagraphStyle](../reference/apis-arkgraphics2d/js-apis-graphics-text.md#paragraphstyle))是对多行文本中每段内容的样式设置,包括断词策略、文本对齐方式、最大行数限制等。开发者可以通过对不同段落进行样式化,以提高文本的可读性和美观性。
227
228
229### 开发步骤
230
2311. 通过context获取到Canvas画布对象。
232
233   ```ts
234   // 绘制代码逻辑写在这里
235   let canvas = context.canvas;
236   ```
237
2382. 初始化文本样式。
239
240   ```ts
241   let myTextStyle: text.TextStyle = {
242     color: {
243       alpha: 255,
244       red: 255,
245       green: 0,
246       blue: 0
247     },
248     fontSize: 50,
249     // 当wordBreak为text.WordBreak.BREAK_HYPHEN时,需要设置语言偏好,段落会在不同语言偏好下呈现不同的文本断词效果
250     locale: "en-gb"
251   };
252   ```
253
2543. 初始化段落样式。
255
256   ```ts
257   let myParagraphStyle: text.ParagraphStyle = {
258     textStyle: myTextStyle,
259     // 文本对齐方式
260     align: text.TextAlign.LEFT,
261     // 最大行数
262     maxLines: 3,
263     // 断词策略
264     wordBreak: text.WordBreak.BREAK_WORD
265   };
266   ```
267
2684. 初始化段落对象,并添加占位符和文本。
269
270   ```ts
271   let fontCollection = text.FontCollection.getGlobalInstance();
272   let paragraphBuilder = new text.ParagraphBuilder(myParagraphStyle, fontCollection);
273   // 更新文本样式
274   paragraphBuilder.pushStyle(myTextStyle);
275   // 添加文本
276   paragraphBuilder.addText("Hello World Hello World Hello World Hello World Hello World Hello World " +
277     "Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World " +
278     "Hello World Hello World Hello World Hello World Hello World ");
279   ```
280
2815. 排版段落并进行文本绘制。
282
283   ```ts
284   // 生成段落
285   let paragraph = paragraphBuilder.build();
286   // 布局
287   paragraph.layoutSync(1250);
288   // 绘制文本
289   paragraph.paint(canvas, 10, 0);
290   ```
291
292
293### 完整示例
294
295```ts
296import { NodeController, FrameNode, RenderNode, DrawContext } from '@kit.ArkUI'
297import { UIContext } from '@kit.ArkUI'
298import { drawing } from '@kit.ArkGraphics2D'
299import { text } from '@kit.ArkGraphics2D'
300import { image } from '@kit.ImageKit'
301import { common2D } from '@kit.ArkGraphics2D'
302
303// 创建一个MyRenderNode类,并绘制文本。
304class MyRenderNode extends RenderNode {
305  async draw(context: DrawContext) {
306    // 绘制代码逻辑写在这里
307    let canvas = context.canvas;
308
309    let myTextStyle: text.TextStyle = {
310      color: {
311        alpha: 255,
312        red: 255,
313        green: 0,
314        blue: 0
315      },
316      fontSize: 50,
317      // 当wordBreak为text.WordBreak.BREAK_HYPHEN时,需要为段落设置语言偏好,段落会在不同语言偏好下呈现不同的文本断词效果
318      locale: "en-gb"
319    };
320
321    let myParagraphStyle: text.ParagraphStyle = {
322      textStyle: myTextStyle,
323      // 文本对齐方式
324      align: text.TextAlign.LEFT,
325      // 最大行数
326      maxLines: 3,
327      // 断词策略
328      wordBreak: text.WordBreak.BREAK_WORD
329    };
330    let fontCollection = text.FontCollection.getGlobalInstance();
331    let paragraphBuilder = new text.ParagraphBuilder(myParagraphStyle, fontCollection);
332    // 更新文本样式
333    paragraphBuilder.pushStyle(myTextStyle);
334    // 添加文本
335    paragraphBuilder.addText("Hello World Hello World Hello World Hello World Hello World Hello World " +
336      "Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World " +
337      "Hello World Hello World Hello World Hello World Hello World ");
338    // 当wordBreak为text.WordBreak.BREAK_HYPHEN时,替换文本内容为:
339    // paragraphBuilder.addText("Modern embedded systems require robust communication protocols and efficient memory " +
340    //   "management strategies. Developers often face challenges in optimizing performance while maintaining " +
341    //   "modularity and portability. By leveraging a layered architecture and structured logging, applications can " +
342    //   "detect anomalies and respond quickly to faults. This approach enhances reliability, especially in " +
343    //   "time-critical environments such as IoT devices and real-time operating systems.");
344
345    // 生成段落
346    let paragraph = paragraphBuilder.build();
347    // 布局
348    paragraph.layoutSync(1250);
349    // 绘制文本
350    paragraph.paint(canvas, 10, 0);
351  }
352}
353
354// 创建一个MyRenderNode对象
355const textNode = new MyRenderNode()
356// 定义newNode的像素格式
357textNode.frame = {
358  x: 0,
359  y: 0,
360  width: 400,
361  height: 600
362}
363textNode.pivot = { x: 0.2, y: 0.8 }
364textNode.scale = { x: 1, y: 1 }
365
366class MyNodeController extends NodeController {
367  private rootNode: FrameNode | null = null;
368
369  makeNode(uiContext: UIContext): FrameNode {
370    this.rootNode = new FrameNode(uiContext)
371    if (this.rootNode == null) {
372      return this.rootNode
373    }
374    const renderNode = this.rootNode.getRenderNode()
375    if (renderNode != null) {
376      renderNode.frame = {
377        x: 0,
378        y: 0,
379        width: 10,
380        height: 500
381      }
382    }
383    return this.rootNode
384  }
385
386  addNode(node: RenderNode): void {
387    if (this.rootNode == null) {
388      return
389    }
390    const renderNode = this.rootNode.getRenderNode()
391    if (renderNode != null) {
392      renderNode.appendChild(node)
393    }
394  }
395
396  clearNodes(): void {
397    if (this.rootNode == null) {
398      return
399    }
400    const renderNode = this.rootNode.getRenderNode()
401    if (renderNode != null) {
402      renderNode.clearChildren()
403    }
404  }
405}
406
407let myNodeController: MyNodeController = new MyNodeController()
408
409async function performTask() {
410  myNodeController.clearNodes()
411  myNodeController.addNode(textNode)
412}
413
414@Entry
415@Component
416struct Font08 {
417  @State src: Resource = $r('app.media.startIcon')
418  build() {
419    Column() {
420      Row() {
421        NodeContainer(myNodeController)
422          .height('100%')
423          .width('100%')
424        Image(this.src)
425          .width('0%').height('0%')
426          .onComplete(
427            () => {
428              performTask();
429            })
430      }
431      .width('100%')
432    }
433  }
434}
435```
436
437
438### 效果展示
439
440| 段落样式设置(断词策略、文本对齐方式、最大行数限制) | 效果示意 |
441| -------- | -------- |
442| 文本对齐方式为text.TextAlign.LEFT,最大行数为3,断词策略为text.WordBreak.BREAK_WORD。 | ![zh-cn_image_0000002246563849](figures/zh-cn_image_0000002246563849.png) |
443| 文本对齐方式为text.TextAlign.RIGHT,最大行数为3,断词策略为text.WordBreak.BREAK_WORD。 | ![zh-cn_image_0000002211443900](figures/zh-cn_image_0000002211443900.png) |
444| 文本对齐方式为text.TextAlign.JUSTIFY,最大行数为10,断词策略为text.WordBreak.BREAK_WORD。 | ![zh-cn_image_complexArkTsDemoJustify](figures/zh-cn_image_complexArkTsDemoJustify.png) |
445| 文本对齐方式为text.TextAlign.LEFT,最大行数为3,断词策略为text.WordBreak.BREAK_ALL。 | ![zh-cn_image_0000002211603680](figures/zh-cn_image_0000002211603680.png) |
446| 文本对齐方式为text.TextAlign.LEFT,最大行数为10,断词策略为text.WordBreak.BREAK_ALL。 | ![zh-cn_image_0000002246563845](figures/zh-cn_image_0000002246563845.png) |
447| 文本对齐方式为text.TextAlign.LEFT,最大行数为10,断词策略为text.WordBreak.BREAK_HYPHEN,<br/>不设置语言偏好。段落无连字符“-”断词效果。 | ![ts_word_break_hyphen_locale_undefined.jpg](figures/ts_word_break_hyphen_locale_undefined.jpg) |
448| 文本对齐方式为text.TextAlign.LEFT,最大行数为10,断词策略为text.WordBreak.BREAK_HYPHEN,<br/>语言偏好为en-gb(英式英语)。段落产生连字符“-”断词效果,并根据语言偏好呈现英式语言环境断词效果。 | ![ts_word_break_hyphen_local_en-gb.jpg](figures/ts_word_break_hyphen_local_en-gb.jpg) |
449| 文本对齐方式为text.TextAlign.LEFT,最大行数为10,断词策略为text.WordBreak.BREAK_HYPHEN,<br/>语言偏好为en-us(美式英语)。段落产生连字符“-”断词效果,并根据语言偏好呈现美式语言环境断词效果。 | ![ts_word_break_hyphen_local_en-us.jpg](figures/ts_word_break_hyphen_local_en-us.jpg) |
450
451
452## 多样式文本绘制与显示
453
454除基本文字、排版属性之外,针对应用中不同文本的设计,开发者可能需要设置使用不同的绘制样式或能力,以凸显对应文本的独特表现或风格,此时可以结合使用多种绘制样式进行文本的渲染。
455
456当前支持的多样式绘制及各绘制样式侧重效果如下:
457
458- **装饰线样式绘制:** 主要通过不同的线条样式对文本进行装饰,可以使文本更加突出,富有表现力。
459
460- **字体特性绘制:** 主要通过字体的变化,包括粗细、斜体等特性来改变文本的外观,增强文本的可读性和美观性。
461
462- **可变字体绘制:** 对应提供文本在不同的显示环境和设备上灵活调整的能力,可满足更为精细的视觉效果。
463
464- **文本阴影绘制:** 主要通过在文本周围添加阴影效果,以提升文本的层次感和立体感,从而使文本更具吸引力。
465
466- **占位符绘制:** 可以在不确定文本内容时保持文本布局的稳定性,使得文本显示更为流畅和自然。
467
468- **自动间距绘制:** 可以在一些字符混排切换的地方自动添加额外间距,提升阅读体验。
469
470- **垂直对齐:** 调整文本在垂直方向排版位置,提升排版质量。
471
472- **上下标:** 可以将任意字符处理成上标或下标,更精准表达文本含义。
473
474- **高对比度文字绘制:** 主要通过将深色文字变黑、浅色文字变白,增强文本的对比效果。
475
476### 装饰线
477
478装饰线([Decoration](../reference/apis-arkgraphics2d/js-apis-graphics-text.md#decoration))是指在文本上方、下方或中间添加的装饰性线条,当前支持上划线、下划线、删除线。
479
480可以通过添加文本装饰线,提升文本的视觉效果和可读性。
481
482使用装饰线需要初始化装饰线样式对象,并添加到文本样式中,从而在文本绘制时生效。
483
484具体使用效果可参见下文[示例一](#示例一装饰线字体特征)。
485
486### 字体特征
487
488**字体特征**([FontFeature](../reference/apis-arkgraphics2d/js-apis-graphics-text.md#fontfeature))绘制专注于在文本渲染过程中对字体特性(如粗体、斜体、字体变种等)的处理,允许字体在不同的排版场景下表现出不同的效果,可用于增强文本的表现力,使其更符合设计和阅读需求。
489
490常见的**FontFeature**包含有liga、frac、case等,需要对应的ttf文件支持才能正常使能。
491
492具体使用效果可参见下文[示例一](#示例一装饰线字体特征)。
493
494### 可变字体
495
496**可变字体**([FontVariation](../reference/apis-arkgraphics2d/js-apis-graphics-text.md#fontvariation))是一种在一个字体文件中包含多个字形变体的字体格式,允许在一个字体文件内灵活地调整字体的各种属性(如字重、字宽、斜体等)。
497
498与传统字体文件(每种变体需要一个独立的文件)不同,可变字体在一个字体文件中包含多个变体轴,可通过使用可变字体实现文本渲染绘制时的平滑过渡。
499
500具体使用效果可参见下文[示例二](#示例二可变字体文本阴影占位符)。
501
502### 文本阴影
503
504**文本阴影**([TextShadow](../reference/apis-arkgraphics2d/js-apis-graphics-text.md#textshadow))为文本提供了深度感,使得文本在背景上更具立体感。通常用于提升文本的视觉吸引力或增强可读性,尤其是在色彩对比度较低的场景下。
505
506其中,TextShadow有三个属性,分别为阴影颜色color、阴影基于当前文本的偏移位置point、阴影半径blurRadius。
507
508使用阴影效果需要在文本样式中设置对应的阴影效果数组,从而在文本绘制时生效。
509
510具体使用效果可参见下文[示例二](#示例二可变字体文本阴影占位符)。
511
512### 占位符
513
514占位符绘制用于处理文本中占位符符号的渲染。
515
516占位符也是用来实现图文混排的关键,是指在实际图像或内容注册之前,用来预先提供或替代某个位置的视觉元素。
517
518具体使用效果可参见下文[示例二](#示例二可变字体文本阴影占位符)。
519
520### 自动间距
521
522使能自动间距,则会在文本排版时自动调整CJK(中文字符、日文字符、韩文字符)与西文(拉丁字母、西里尔字母、希腊字母)、CJK与数字、CJK与版权符号、版权符号与数字、版权符号与西文之间的间距。例如,在中英文混排场景中,使能自动间距即可在中英文切换的地方自动添加额外间距,提升阅读体验。
523关键示例如下:
524```ts
525let myParagraphStyle: text.ParagraphStyle = {
526  autoSpace: true
527};
528```
529
530### 垂直对齐
531
532垂直对齐用于调整文本在一行中垂直方向的排版位置。开启行高缩放或行内存在不同字号文本混排时使能垂直对齐,可以让文本实现顶部对齐、居中对齐、底部对齐或基线对齐(默认)。关键代码如下:
533
534```ts
535let myParagraphStyle: text.ParagraphStyle = {
536  verticalAlign: text.TextVerticalAlign.CENTER
537};
538```
539
540具体使用效果可参见下文[示例三](#示例三垂直对齐)。
541
542### 上下标
543
544使能上下标,能将文本作为上标或下标参与排版。一般用于数学公式、化学式等场景。关键代码如下:
545
546```ts
547let superScriptStyle: text.TextStyle = {
548    badgeType: text.TextBadgeType.TEXT_SUPERSCRIPT
549};
550```
551
552具体使用效果可参见下文[示例四](#示例四上下标文本)。
553
554### 高对比度
555
556高对比度可将深色文字变黑、浅色文字变白。开发者可选择开启或关闭应用的高对比度文字渲染,或遵循系统设置中的高对比度文字配置。
557
558高对比度模式有3种,具体参考[TextHighContrast](../reference/apis-arkgraphics2d/js-apis-graphics-text.md#texthighcontrast20)。
559
560具体使用效果可参见下文[示例五](#示例五高对比度)。
561
562### 示例一(装饰线、字体特征)
563这里以文本样式中的装饰线和字体特征为例,呈现多样式文本的绘制与显示。
564
565```ts
566import { NodeController, FrameNode, RenderNode, DrawContext } from '@kit.ArkUI'
567import { UIContext } from '@kit.ArkUI'
568import { drawing } from '@kit.ArkGraphics2D'
569import { text } from '@kit.ArkGraphics2D'
570import { image } from '@kit.ImageKit'
571import { common2D } from '@kit.ArkGraphics2D'
572
573// 创建一个MyRenderNode类,并绘制文本。
574class MyRenderNode extends RenderNode {
575  async draw(context: DrawContext) {
576    let canvas = context.canvas;
577
578    // 初始化装饰线对象
579    let decorations: text.Decoration =
580      {
581        // 装饰线类型,支持上划线、下划线、删除线
582        textDecoration: text.TextDecorationType.UNDERLINE,
583        // 装饰线颜色
584        color: {
585          alpha: 255,
586          red: 255,
587          green: 0,
588          blue: 0
589        },
590        // 装饰线样式,支持波浪,虚线,直线等
591        decorationStyle:text.TextDecorationStyle.SOLID,
592        // 装饰线的高度
593        decorationThicknessScale: 1
594      };
595
596    let myTextStyle: text.TextStyle = {
597      color: {
598        alpha: 255,
599        red: 255,
600        green: 0,
601        blue: 0
602      },
603      fontSize: 300,
604      // 设置装饰线
605      decoration: decorations,
606      // 开启字体特征
607      fontFeatures: [{name: 'frac', value: 1}]
608    };
609
610    let myParagraphStyle: text.ParagraphStyle = {
611      textStyle: myTextStyle,
612    };
613
614    let fontCollection = text.FontCollection.getGlobalInstance();
615    let paragraphBuilder = new text.ParagraphBuilder(myParagraphStyle, fontCollection);
616
617    // 更新文本样式
618    paragraphBuilder.pushStyle(myTextStyle);
619    // 添加文本
620    paragraphBuilder.addText("1/2 1/3 1/4 ");
621
622    // 生成段落
623    let paragraph = paragraphBuilder.build();
624    // 布局
625    paragraph.layoutSync(1250);
626    // 绘制文本
627    paragraph.paint(canvas, 0, 0);
628  }
629}
630
631// 创建一个MyRenderNode对象
632const textNode = new MyRenderNode()
633// 定义newNode的像素格式
634textNode.frame = {
635  x: 0,
636  y: 0,
637  width: 400,
638  height: 600
639}
640textNode.pivot = { x: 0.2, y: 0.8 }
641textNode.scale = { x: 1, y: 1 }
642
643class MyNodeController extends NodeController {
644  private rootNode: FrameNode | null = null;
645
646  makeNode(uiContext: UIContext): FrameNode {
647    this.rootNode = new FrameNode(uiContext)
648    if (this.rootNode == null) {
649      return this.rootNode
650    }
651    const renderNode = this.rootNode.getRenderNode()
652    if (renderNode != null) {
653      renderNode.frame = {
654        x: 0,
655        y: 0,
656        width: 10,
657        height: 500
658      }
659    }
660    return this.rootNode
661  }
662
663  addNode(node: RenderNode): void {
664    if (this.rootNode == null) {
665      return
666    }
667    const renderNode = this.rootNode.getRenderNode()
668    if (renderNode != null) {
669      renderNode.appendChild(node)
670    }
671  }
672
673  clearNodes(): void {
674    if (this.rootNode == null) {
675      return
676    }
677    const renderNode = this.rootNode.getRenderNode()
678    if (renderNode != null) {
679      renderNode.clearChildren()
680    }
681  }
682}
683
684let myNodeController: MyNodeController = new MyNodeController()
685
686async function performTask() {
687  myNodeController.clearNodes()
688  myNodeController.addNode(textNode)
689}
690
691@Entry
692@Component
693struct Font08 {
694  @State src: Resource = $r('app.media.startIcon')
695  build() {
696    Column() {
697      Row() {
698        NodeContainer(myNodeController)
699          .height('100%')
700          .width('100%')
701        Image(this.src)
702          .width('0%').height('0%')
703          .onComplete(
704            () => {
705              performTask();
706            })
707      }
708      .width('100%')
709    }
710  }
711}
712```
713
714
715具体示意效果如下所示:
716
717| 样式设置(装饰线样式、字体特征) | 示意效果 |
718| -------- | -------- |
719| 不开启装饰线和字体特征 | ![zh-cn_image_complexArkTsDemo1_1](figures/zh-cn_image_complexArkTsDemo1_1.png) |
720| 开启装饰线和字体特征 | ![zh-cn_image_complexArkTsDemo1_2](figures/zh-cn_image_complexArkTsDemo1_2.png) |
721
722### 示例二(可变字体、文本阴影、占位符)
723这里以可变字体、文本阴影、占位符三个特性为例,呈现多样式文本的绘制与显示。
724```ts
725import { NodeController, FrameNode, RenderNode, DrawContext } from '@kit.ArkUI'
726import { UIContext } from '@kit.ArkUI'
727import { drawing } from '@kit.ArkGraphics2D'
728import { text } from '@kit.ArkGraphics2D'
729import { image } from '@kit.ImageKit'
730import { common2D } from '@kit.ArkGraphics2D'
731
732// 创建一个MyRenderNode类,并绘制文本。
733class MyRenderNode extends RenderNode {
734  async draw(context: DrawContext) {
735    let canvas = context.canvas;
736
737    let myTextStyle: text.TextStyle = {
738      color: {
739        alpha: 255,
740        red: 255,
741        green: 0,
742        blue: 0
743      },
744      fontSize: 150,
745      // 可变字体
746      fontVariations: [{axis: 'wght', value: 555}],
747      // 文本阴影
748      textShadows: [{color: { alpha: 0xFF, red: 0xFF, green: 0x00, blue: 0x00 }, point: {x:10,y:10}, blurRadius: 10}],
749    };
750
751    let myParagraphStyle: text.ParagraphStyle = {
752      textStyle: myTextStyle,
753    };
754
755    let fontCollection = text.FontCollection.getGlobalInstance();
756    let paragraphBuilder = new text.ParagraphBuilder(myParagraphStyle, fontCollection);
757
758    // 初始化占位符对象
759    let myPlaceholderSpan: text.PlaceholderSpan = {
760      // 宽度
761      width: 300,
762      // 高度
763      height: 300,
764      // 基线对齐策略
765      align: text.PlaceholderAlignment.BOTTOM_OF_ROW_BOX,
766      // 使用的文本基线类型
767      baseline: text.TextBaseline.ALPHABETIC,
768      // 相比基线的偏移量。只有对齐策略是OFFSET_AT_BASELINE时生效
769      baselineOffset: 100
770    };
771    // 添加占位符
772    paragraphBuilder.addPlaceholder(myPlaceholderSpan)
773
774    // 更新文本样式
775    paragraphBuilder.pushStyle(myTextStyle);
776    // 添加文本
777    paragraphBuilder.addText("Hello Test");
778
779    // 生成段落
780    let paragraph = paragraphBuilder.build();
781    // 布局
782    paragraph.layoutSync(1250);
783    // 绘制文本
784    paragraph.paint(canvas, 0, 0);
785
786    //获取全部占位符的数组
787    let placeholderRects = paragraph.getRectsForPlaceholders();
788    // 获取第一个占位符的左边界
789    let left = placeholderRects[0].rect.left
790    // 获取第一个占位符的上边界
791    let top = placeholderRects[0].rect.top
792    // 获取第一个占位符的右边界
793    let right = placeholderRects[0].rect.right
794    // 获取第一个占位符的下边界
795    let bottom = placeholderRects[0].rect.bottom
796    let pen: drawing.Pen =  new drawing.Pen()
797    let pen_color : common2D.Color = { alpha: 0xFF, red: 0xFF, green: 0x00, blue: 0x00 }
798    pen.setColor(pen_color)
799    canvas.attachPen(pen)
800    // 使用draw方法绘制占位符矩形框
801    canvas.drawRect(left,top,right,bottom)
802  }
803}
804
805// 创建一个MyRenderNode对象
806const textNode = new MyRenderNode()
807// 定义newNode的像素格式
808textNode.frame = {
809  x: 0,
810  y: 0,
811  width: 400,
812  height: 600
813}
814textNode.pivot = { x: 0.2, y: 0.8 }
815textNode.scale = { x: 1, y: 1 }
816
817class MyNodeController extends NodeController {
818  private rootNode: FrameNode | null = null;
819
820  makeNode(uiContext: UIContext): FrameNode {
821    this.rootNode = new FrameNode(uiContext)
822    if (this.rootNode == null) {
823      return this.rootNode
824    }
825    const renderNode = this.rootNode.getRenderNode()
826    if (renderNode != null) {
827      renderNode.frame = {
828        x: 0,
829        y: 0,
830        width: 10,
831        height: 500
832      }
833    }
834    return this.rootNode
835  }
836
837  addNode(node: RenderNode): void {
838    if (this.rootNode == null) {
839      return
840    }
841    const renderNode = this.rootNode.getRenderNode()
842    if (renderNode != null) {
843      renderNode.appendChild(node)
844    }
845  }
846
847  clearNodes(): void {
848    if (this.rootNode == null) {
849      return
850    }
851    const renderNode = this.rootNode.getRenderNode()
852    if (renderNode != null) {
853      renderNode.clearChildren()
854    }
855  }
856}
857
858let myNodeController: MyNodeController = new MyNodeController()
859
860async function performTask() {
861  myNodeController.clearNodes()
862  myNodeController.addNode(textNode)
863}
864
865@Entry
866@Component
867struct Font08 {
868  @State src: Resource = $r('app.media.startIcon')
869  build() {
870    Column() {
871      Row() {
872        NodeContainer(myNodeController)
873          .height('100%')
874          .width('100%')
875        Image(this.src)
876          .width('0%').height('0%')
877          .onComplete(
878            () => {
879              performTask();
880            })
881      }
882      .width('100%')
883    }
884  }
885}
886```
887
888具体示意效果如下所示:
889
890| 样式设置(可变字体、文本阴影、占位符) | 示意效果 |
891| -------- | -------- |
892| 不开启可变字体和文本阴影,不使用占位符 | ![zh-cn_image_complexArkTsDemo2_1](figures/zh-cn_image_complexArkTsDemo2_1.png) |
893| 开启可变字体和文本阴影,使用占位符 | ![zh-cn_image_complexArkTsDemo2_2](figures/zh-cn_image_complexArkTsDemo2_2.png) |
894
895### 示例三(垂直对齐)
896这里以垂直对齐-居中对齐特性为例,呈现文本垂直方向排版的特性。
897
898```ts
899import { NodeController, FrameNode, RenderNode, DrawContext, UIContext } from '@kit.ArkUI'
900import { drawing, text, common2D } from '@kit.ArkGraphics2D'
901import { image } from '@kit.ImageKit'
902
903// 创建一个MyRenderNode类,并绘制文本。
904class MyRenderNode extends RenderNode {
905  async draw(context: DrawContext) {
906    let canvas = context.canvas;
907
908    let myTextStyle: text.TextStyle = {
909      color: {
910        alpha: 255,
911        red: 255,
912        green: 0,
913        blue: 0
914      },
915      fontSize: 30,
916      // 开启行高缩放
917      heightOnly: true,
918      // 行高缩放系数为字号的2倍
919      heightScale: 2
920    };
921
922    let myParagraphStyle: text.ParagraphStyle = {
923      textStyle: myTextStyle,
924      // 设置垂直对齐-居中对齐模式
925      verticalAlign: text.TextVerticalAlign.CENTER,
926    };
927
928    let fontCollection = text.FontCollection.getGlobalInstance();
929    let paragraphBuilder = new text.ParagraphBuilder(myParagraphStyle, fontCollection);
930
931    // 设置待排版文本要应用的样式
932    paragraphBuilder.pushStyle(myTextStyle);
933    // 添加文本
934    paragraphBuilder.addText("VerticalAlignment-center");
935
936    // 生成段落
937    let paragraph = paragraphBuilder.build();
938    // 布局
939    paragraph.layoutSync(1000);
940    // 绘制文本
941    paragraph.paint(canvas, 0, 0);
942  }
943}
944
945// 创建一个MyRenderNode对象
946const textNode = new MyRenderNode()
947// 定义newNode的像素格式
948textNode.frame = {
949  x: 0,
950  y: 0,
951  width: 400,
952  height: 600
953}
954textNode.pivot = { x: 0.2, y: 0.8 }
955textNode.scale = { x: 1, y: 1 }
956
957class MyNodeController extends NodeController {
958  private rootNode: FrameNode | null = null;
959
960  makeNode(uiContext: UIContext): FrameNode {
961    this.rootNode = new FrameNode(uiContext)
962    if (this.rootNode == null) {
963      return this.rootNode
964    }
965    const renderNode = this.rootNode.getRenderNode()
966    if (renderNode != null) {
967      renderNode.frame = {
968        x: 0,
969        y: 0,
970        width: 10,
971        height: 500
972      }
973      renderNode.pivot = { x: 50, y: 50 }
974    }
975    return this.rootNode
976  }
977
978  addNode(node: RenderNode): void {
979    if (this.rootNode == null) {
980      return
981    }
982    const renderNode = this.rootNode.getRenderNode()
983    if (renderNode != null) {
984      renderNode.appendChild(node)
985    }
986  }
987
988  clearNodes(): void {
989    if (this.rootNode == null) {
990      return
991    }
992    const renderNode = this.rootNode.getRenderNode()
993    if (renderNode != null) {
994      renderNode.clearChildren()
995    }
996  }
997}
998
999let myNodeController: MyNodeController = new MyNodeController()
1000
1001async function performTask() {
1002  myNodeController.clearNodes()
1003  myNodeController.addNode(textNode)
1004}
1005
1006@Entry
1007@Component
1008struct Font08 {
1009  @State src: Resource = $r('app.media.startIcon')
1010  build() {
1011    Column() {
1012      Row() {
1013        NodeContainer(myNodeController)
1014          .height('100%')
1015          .width('100%')
1016        Text("Test for vertical alignment")
1017          .onApper(() => {
1018            performTask();
1019          })
1020      }
1021      .width('100%')
1022    }
1023  }
1024}
1025```
1026
1027具体示意效果如下所示:
1028| 样式设置(垂直对齐) | 示意效果 |
1029| -------- | -------- |
1030| 基线对齐(默认)| ![zh-cn_image_complexArkTsDemo2_1](figures/en_image_verticalAlignment_baseline.jpg) |
1031| 顶部对齐 | ![zh-cn_image_complexArkTsDemo2_2](figures/en_image_verticalAlignment_top.jpg) |
1032| 居中对齐 | ![zh-cn_image_complexArkTsDemo2_2](figures/en_image_verticalAlignment_center.jpg) |
1033| 底部对齐 | ![zh-cn_image_complexArkTsDemo2_2](figures/en_image_verticalAlignment_bottom.jpg) |
1034
1035### 示例四(上下标文本)
1036这里以下标样式为例,呈现上下标文本排版特性。
1037
1038```ts
1039import { NodeController, FrameNode, RenderNode, DrawContext, UIContext } from '@kit.ArkUI'
1040import { drawing, text, common2D } from '@kit.ArkGraphics2D'
1041import { image } from '@kit.ImageKit'
1042
1043// 创建一个MyRenderNode类,并绘制文本。
1044class MyRenderNode extends RenderNode {
1045  async draw(context: DrawContext) {
1046    let canvas = context.canvas;
1047
1048    let myTextStyle: text.TextStyle = {
1049      color: {
1050        alpha: 255,
1051        red: 255,
1052        green: 0,
1053        blue: 0
1054      },
1055      fontSize: 30,
1056    };
1057
1058    let subScriptStyle: text.TextStyle = {
1059      color: {
1060        alpha: 255,
1061        red: 255,
1062        green: 0,
1063        blue: 0
1064      },
1065      fontSize: 30,
1066      // 设置下标样式
1067      badgeType: text.TextBadgeType.TEXT_SUBSCRIPT
1068    };
1069
1070    let myParagraphStyle: text.ParagraphStyle = {
1071      textStyle: myTextStyle,
1072    };
1073
1074    let fontCollection = text.FontCollection.getGlobalInstance();
1075    let paragraphBuilder = new text.ParagraphBuilder(myParagraphStyle, fontCollection);
1076
1077    // 设置待排版文本要应用的样式
1078    paragraphBuilder.pushStyle(myTextStyle);
1079    // 添加文本
1080    paragraphBuilder.addText("The chemical formula for water: H");
1081    paragraphBuilder.pushStyle(subScriptStyle);
1082    paragraphBuilder.addText("2");
1083    paragraphBuilder.pushStyle(myTextStyle);
1084    paragraphBuilder.addText("o");
1085
1086    // 生成段落
1087    let paragraph = paragraphBuilder.build();
1088    // 布局
1089    paragraph.layoutSync(1000);
1090    // 绘制文本
1091    paragraph.paint(canvas, 0, 0);
1092  }
1093}
1094
1095// 创建一个MyRenderNode对象
1096const textNode = new MyRenderNode()
1097// 定义newNode的像素格式
1098textNode.frame = {
1099  x: 0,
1100  y: 0,
1101  width: 400,
1102  height: 600
1103}
1104textNode.pivot = { x: 0.2, y: 0.8 }
1105textNode.scale = { x: 1, y: 1 }
1106
1107class MyNodeController extends NodeController {
1108  private rootNode: FrameNode | null = null;
1109
1110  makeNode(uiContext: UIContext): FrameNode {
1111    this.rootNode = new FrameNode(uiContext)
1112    if (this.rootNode == null) {
1113      return this.rootNode
1114    }
1115    const renderNode = this.rootNode.getRenderNode()
1116    if (renderNode != null) {
1117      renderNode.frame = {
1118        x: 0,
1119        y: 0,
1120        width: 10,
1121        height: 500
1122      }
1123      renderNode.pivot = { x: 50, y: 50 }
1124    }
1125    return this.rootNode
1126  }
1127
1128  addNode(node: RenderNode): void {
1129    if (this.rootNode == null) {
1130      return
1131    }
1132    const renderNode = this.rootNode.getRenderNode()
1133    if (renderNode != null) {
1134      renderNode.appendChild(node)
1135    }
1136  }
1137
1138  clearNodes(): void {
1139    if (this.rootNode == null) {
1140      return
1141    }
1142    const renderNode = this.rootNode.getRenderNode()
1143    if (renderNode != null) {
1144      renderNode.clearChildren()
1145    }
1146  }
1147}
1148
1149let myNodeController: MyNodeController = new MyNodeController()
1150
1151async function performTask() {
1152  myNodeController.clearNodes()
1153  myNodeController.addNode(textNode)
1154}
1155
1156@Entry
1157@Component
1158struct Font08 {
1159  @State src: Resource = $r('app.media.startIcon')
1160  build() {
1161    Column() {
1162      Row() {
1163        NodeContainer(myNodeController)
1164          .height('100%')
1165          .width('100%')
1166        Text("Test for superscript and subscript")
1167          .onApper(() => {
1168            performTask();
1169          })
1170      }
1171    }
1172      .width('100%')
1173  }
1174}
1175```
1176
1177具体示意效果如下所示:
1178| 样式设置(上下标) | 示意效果 |
1179| -------- | -------- |
1180| 上标文本 | ![zh-cn_image_complexArkTsDemo2_1](figures/en_image_subscript.jpg) |
1181| 下标文本 | ![zh-cn_image_complexArkTsDemo2_2](figures/en_image_superscript.jpg) |
1182
1183### 示例五(高对比度)
1184这里以高对比度为例,呈现高对比度文字的绘制与显示。
1185```ts
1186import { NodeController, FrameNode, RenderNode, DrawContext, UIContext} from '@kit.ArkUI'
1187import { text } from '@kit.ArkGraphics2D'
1188
1189// 创建一个MyRenderNode类,并绘制文本。
1190class MyRenderNode extends RenderNode {
1191  async draw(context: DrawContext) {
1192    let canvas = context.canvas;
1193
1194    // 开启APP的文字渲染高对比度配置
1195    text.setTextHighContrast(text.TextHighContrast.TEXT_APP_ENABLE_HIGH_CONTRAST);
1196
1197    let myTextStyle: text.TextStyle = {
1198      color: {
1199        alpha: 255,
1200        red: 111,
1201        green: 255,
1202        blue: 255
1203      },
1204      fontSize: 100,
1205    };
1206
1207    let myParagraphStyle: text.ParagraphStyle = {
1208      textStyle: myTextStyle,
1209    };
1210
1211    let fontCollection = text.FontCollection.getGlobalInstance();
1212    let paragraphBuilder = new text.ParagraphBuilder(myParagraphStyle, fontCollection);
1213
1214    // 更新文本样式
1215    paragraphBuilder.pushStyle(myTextStyle);
1216    // 添加文本
1217    paragraphBuilder.addText("Hello World");
1218
1219    // 生成段落
1220    let paragraph = paragraphBuilder.build();
1221    // 布局
1222    paragraph.layoutSync(1250);
1223    // 绘制文本
1224    paragraph.paint(canvas, 10, 800);
1225  }
1226}
1227
1228// 创建一个MyRenderNode对象
1229const textNode = new MyRenderNode()
1230// 定义newNode的像素格式
1231textNode.frame = {
1232  x: 0,
1233  y: 0,
1234  width: 400,
1235  height: 600
1236}
1237textNode.pivot = { x: 0.2, y: 0.8 }
1238textNode.scale = { x: 1, y: 1 }
1239
1240class MyNodeController extends NodeController {
1241  private rootNode: FrameNode | null = null;
1242
1243  makeNode(uiContext: UIContext): FrameNode {
1244    this.rootNode = new FrameNode(uiContext)
1245    if (this.rootNode == null) {
1246      return this.rootNode
1247    }
1248    const renderNode = this.rootNode.getRenderNode()
1249    if (renderNode != null) {
1250      renderNode.frame = {
1251        x: 0,
1252        y: 0,
1253        width: 10,
1254        height: 500
1255      }
1256      renderNode.pivot = { x: 0.2, y: 0.8 }
1257    }
1258    return this.rootNode
1259  }
1260
1261  addNode(node: RenderNode): void {
1262    if (this.rootNode == null) {
1263      return
1264    }
1265    const renderNode = this.rootNode.getRenderNode()
1266    if (renderNode != null) {
1267      renderNode.appendChild(node)
1268    }
1269  }
1270
1271  clearNodes(): void {
1272    if (this.rootNode == null) {
1273      return
1274    }
1275    const renderNode = this.rootNode.getRenderNode()
1276    if (renderNode != null) {
1277      renderNode.clearChildren()
1278    }
1279  }
1280}
1281
1282let myNodeController: MyNodeController = new MyNodeController()
1283
1284async function performTask() {
1285  myNodeController.clearNodes()
1286  myNodeController.addNode(textNode)
1287}
1288
1289@Entry
1290@Component
1291struct Font08 {
1292  build() {
1293    Column() {
1294      Row() {
1295        NodeContainer(myNodeController)
1296          .height('100%')
1297          .width('100%')
1298        Text("Test high contrast")
1299          .onAppear(() => {
1300              performTask();
1301            })
1302      }
1303      .width('100%')
1304    }
1305  }
1306}
1307```
1308具体示意效果如下所示:
1309
1310| 高对比度设置 | 示意效果 |
1311| -------- | -------- |
1312| 不开启高对比度 | ![zh-cn_image_complexArkTsDemo5_1](figures/zh-cn_image_complexArkTsDemo5_1.png) |
1313| 开启高对比度 | ![zh-cn_image_complexArkTsDemo5_2](figures/zh-cn_image_complexArkTsDemo5_2.png) |