• 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
25## 接口说明
26
27文本测量中常用接口如下表所示,详细接口说明参考[@ohos.graphics.text (文本模块)](../reference/apis-arkgraphics2d/js-apis-graphics-text.md#paragraph)。
28
29| 接口名 | 描述 |
30| -------- | -------- |
31| getLongestLine(): number | 获取当前段落最长行的宽度,建议实际使用时将返回值向上取整。 |
32| getLongestLineWithIndent(): number | 获取当前段落最长行的宽度(该宽度包含当前行缩进的宽度),建议实际使用时将返回值向上取整。 |
33| getTextLines(): Array&lt;TextLine&gt; | 获取当前段落文本行对象数组。 |
34| getLineMetrics(): Array&lt;LineMetrics&gt; | 获取段落所有行的度量信息。包含行的高度、宽度、起始坐标等信息。 |
35| getLineMetrics(lineNumber: number): LineMetrics \| undefined | 获取段落指定行的度量信息。包含行的高度、宽度、起始坐标等信息。超出当前段落排版后最大行数后返回undefined。 |
36
37
38## 开发步骤
39
401. 导入依赖的相关模块。
41
42   ```ts
43   import { text } from '@kit.ArkGraphics2D';
44   ```
45
462. 创建段落样式,并使用构造段落生成器ParagraphBuilder生成段落实例。
47
48   ```ts
49   // 设置文本样式
50   let myTextStyle: text.TextStyle = {
51       color: { alpha: 255, red: 255, green: 0, blue: 0 },
52       fontSize: 100
53   };
54   // 创建一个段落样式对象,以设置排版风格
55   let myParagraphStyle: text.ParagraphStyle = {
56       textStyle: myTextStyle,
57       wordBreak:text.WordBreak.NORMAL
58   }
59   // 创建一个段落生成器
60   let paragraphBuilder: text.ParagraphBuilder = new text.ParagraphBuilder(myParagraphStyle, new text.FontCollection());
61   ```
62
633. 设置文本样式,添加文本内容,并生成段落文本用于后续文本的绘制显示。
64
65   ```ts
66   // 在段落生成器中设置文本样式
67   paragraphBuilder.pushStyle(myTextStyle);
68   // 在段落生成器中设置文本内容
69   paragraphBuilder.addText("文本测量测试");
70   // 通过段落生成器生成段落
71   let paragraph = paragraphBuilder.build();
72   ```
73
744. 调用测量相关接口,获取指定的测量信息。
75
76   ```ts
77   // case1: 获取排版后最长行行宽
78   let longestLineWidth = paragraph.getLongestLine();
79   // case2: 获取排版后最长行行宽(包含缩进)
80   let longestLineWithIndentWidth = paragraph.getLongestLineWithIndent()
81   // case3: 获取排版后所有行对象
82   let textLines = paragraph.getTextLines();
83   // case4: 获取排版后指定行对应的度量信息
84   let lineCnt = paragraph.getLineCount();
85   for (let index = 0; index < lineCnt; index++) {
86       let lineMetrics = paragraph.getLineMetrics(index);
87   }
88   // case5: 获取排版后所有行度量信息数组
89   let allLineMetrics = paragraph.getLineMetrics();
90   ```
91
92## 完整示例
93
94完整的文本测量示例如下。
95
96```ts
97// Index.ets
98import { NodeController, FrameNode, RenderNode, DrawContext } from '@kit.ArkUI';
99import { UIContext } from '@kit.ArkUI';
100import { text } from '@kit.ArkGraphics2D';
101
102// 创建一个自定义的渲染节点类,用于绘制文本
103class MyRenderNode extends RenderNode {
104  async draw(context: DrawContext) {
105    // 获取画布canvas对象
106    const canvas = context.canvas;
107    // 配置文本样式(红色,字号100px)
108    let myTextStyle: text.TextStyle = {
109      color: {
110        alpha: 255,
111        red: 255,
112        green: 0,
113        blue: 0
114      },
115      fontSize: 100
116    };
117    // 创建一个段落样式对象,以设置排版风格
118    let myParagraphStyle: text.ParagraphStyle = {
119      textStyle: myTextStyle,
120      wordBreak: text.WordBreak.NORMAL
121    };
122    // 创建一个段落生成器
123    let paragraphBuilder = new text.ParagraphBuilder(myParagraphStyle, new text.FontCollection());
124    // 在段落生成器中设置文本样式
125    paragraphBuilder.pushStyle(myTextStyle);
126    // 在段落生成器中设置文本内容
127    paragraphBuilder.addText("文本测量测试");
128    // 通过段落生成器生成段落
129    let paragraph = paragraphBuilder.build();
130    // 布局
131    paragraph.layoutSync(1000);
132    // case1: 获取排版后最长行行宽
133    let longestLineWidth = paragraph.getLongestLine();
134    console.info("longestLineWidth = " + longestLineWidth);
135
136    // case2: 获取排版后最长行行宽(包含缩进)
137    let longestLineWithIndentWidth = paragraph.getLongestLineWithIndent();
138    console.info("longestLineWithIndentWidth = " + longestLineWithIndentWidth);
139
140    // case3: 获取排版后所有行对象
141    let textLines = paragraph.getTextLines();
142    for (let index = 0; index < textLines.length; index++) {
143      const textline = textLines[index];
144      let curLineRange = textline.getTextRange();
145      let curLineGlyCnt = textline.getGlyphCount();
146      console.info("MetricsMSG: 第" + (index + 1) + "行 TextRange start: " + curLineRange.start + " TextRange end: " + curLineRange.end);
147      console.info("MetricsMSG: 第" + (index + 1) + "行字形数量为: " + curLineGlyCnt);
148    }
149
150    // case4: 获取排版后指定行对应的度量信息
151    let lineCnt = paragraph.getLineCount();
152    for (let index = 0; index < lineCnt; index++) {
153      let lineMetrics = paragraph.getLineMetrics(index);
154      if (lineMetrics) {
155        console.info("MetricsMSG: 第" + (index + 1) + "行 lineMetrics width: " + lineMetrics.width);
156        console.info("MetricsMSG: 第" + (index + 1) + "行 lineMetrics start index: " + lineMetrics.startIndex + ", end index: " +
157        lineMetrics.endIndex);
158      }
159    }
160
161    // case5: 获取排版后所有行度量信息数组
162    let allLineMetrics = paragraph.getLineMetrics();
163    console.info("MetricsMSG: 第1行 lineMetrics width: " + allLineMetrics[0].width);
164    paragraph.paint(canvas, 0, 800);
165  }
166}
167
168// 创建文本渲染节点实例
169const textNode = new MyRenderNode();
170// 定义newNode的大小和位置
171textNode.frame = { x: 0, y: 0, width: 400, height: 600 };
172
173class MyNodeController extends NodeController {
174  private rootNode: FrameNode | null = null;
175
176  makeNode(uiContext: UIContext): FrameNode {
177    this.rootNode = new FrameNode(uiContext);
178    if (this.rootNode == null) {
179      return this.rootNode;
180    }
181    const renderNode = this.rootNode.getRenderNode();
182    if (renderNode != null) {
183      renderNode.frame = {
184        x: 0,
185        y: 0,
186        width: 300,
187        height: 50
188      };
189      renderNode.pivot = { x: 0, y: 0 };
190    }
191    return this.rootNode;
192  }
193
194  addNode(node: RenderNode): void {
195    if (this.rootNode == null) {
196      return;
197    }
198    const renderNode = this.rootNode.getRenderNode();
199    if (renderNode != null) {
200      renderNode.appendChild(node);
201    }
202  }
203
204  clearNodes(): void {
205    if (this.rootNode == null) {
206      return;
207    }
208    const renderNode = this.rootNode.getRenderNode();
209    if (renderNode != null) {
210      renderNode.clearChildren();
211    }
212  }
213}
214
215@Entry
216@Component
217struct RenderTest {
218  private myNodeController: MyNodeController = new MyNodeController();
219
220  build() {
221    Column() {
222      Row() {
223        NodeContainer(this.myNodeController)
224          .height('100%')
225          .position({ x: 30, y: 25 })
226      }
227
228        Row() {
229          Button("Draw Text")
230            .fontSize('16fp')
231            .fontWeight(500)
232            .margin({ bottom: 24, right: 12 })
233            .onClick(() => {
234              this.myNodeController.clearNodes();
235              this.myNodeController.addNode(textNode);
236            })
237            .width('50%')
238            .height(40)
239        }
240        .width('100%')
241        .justifyContent(FlexAlign.Center) // 设置当前Row容器内子元素在主轴上居中对齐
242        .alignItems(VerticalAlign.Bottom) // 设置当前Row容器内子元素在交叉轴(垂直方向)上的对齐方式为底部对齐
243        .layoutWeight(1) // 设置当前Row在父容器Column中的布局权重为1
244      }
245    }
246  }
247```
248