• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 文本测量(ArkTS)
2
3
4## 场景介绍
5
6文本测量指的是在图形绘制中,对文本的尺寸和布局进行评估,计算文本在给定字体和样式下占用的空间(例如宽度、高度和其他相关信息)的过程。文本测量用于文本排版、布局、渲染以及调整文本显示的位置和大小等场景,便于对界面的布局和呈现更好地把控和调整,以达到设计预期。
7
8当前主要支持以下方面的文本测量能力:
9
10
11- **文本宽度**:测量给定文本在特定字体、大小和样式下的水平长度。
12
13- **文本高度**:测量给定文本的垂直高度,通常涉及文本的上升线、下降线等。
14
15- **行间距**:测量多行文本之间的垂直距离,通常与文本的行距相关。
16
17- **字符间距**:测量单个字符之间的水平距离,通常与字形和字体设计有关。
18
19
20## 接口说明
21
22文本测量中常用接口如下表所示,详细接口说明参考[@ohos.graphics.text (文本模块)](../reference/apis-arkgraphics2d/js-apis-graphics-text.md#paragraph)。
23
24| 接口名 | 描述 |
25| -------- | -------- |
26| getLongestLine(): number | 获取当前段落最长行的宽度,建议实际使用时将返回值向上取整。 |
27| getLongestLineWithIndent(): number | 获取当前段落最长行的宽度(该宽度包含当前行缩进的宽度),建议实际使用时将返回值向上取整。 |
28| getTextLines(): Array<TextLine> | 获取当前段落文本行对象数组。 |
29| getLineMetrics(): Array<LineMetrics> | 获取段落所有行的度量信息。包含行的高度、宽度、起始坐标等信息。 |
30| getLineMetrics(lineNumber: number): LineMetrics \| undefined | 获取段落指定行的度量信息。包含行的高度、宽度、起始坐标等信息。超出当前段落排版后最大行数后返回 undefined |
31
32
33## 开发步骤
34
351. 导入依赖的相关模块。
36
37   ```ts
38   import { text } from '@kit.ArkGraphics2D';
39   ```
40
412. 创建段落样式,并使用构造段落生成器PargraphBuilder生成段落实例。
42
43   ```ts
44   // 设置文本样式
45   let myTextStyle: text.TextStyle = {
46       color: { alpha: 255, red: 255, green: 0, blue: 0 },
47       fontSize: 100
48   };
49   // 创建一个段落样式对象,以设置排版风格
50   let myParagraphStyle: text.ParagraphStyle = {
51       textStyle: myTextStyle,
52       wordBreak:text.WordBreak.NORMAL
53   }
54   // 创建一个段落生成器
55   let paragraphBuilder: text.ParagraphBuilder = new text.ParagraphBuilder(myParagraphStyle, new text.FontCollection());
56   ```
57
583. 设置文本样式,添加文本内容,并生成段落文本用于后续文本的绘制显示。
59
60   ```ts
61   // 在段落生成器中设置文本样式
62   paragraphBuilder.pushStyle(myTextStyle);
63   // 在段落生成器中设置文本内容
64   paragraphBuilder.addText("文本测量测试");
65   // 通过段落生成器生成段落
66   let paragraph = paragraphBuilder.build();
67   ```
68
694. 调用测量相关接口,获取指定的测量信息。
70
71   ```ts
72   // case1: 获取排版后最长行行宽
73   let longestLineWidth = paragraph.getLongestLine();
74   // case2: 获取排版后最长行行宽(包含缩进)
75   let longestLineWithIndentWidth = paragraph.getLongestLineWithIndent()
76   // case3: 获取排版后所有行对象
77   let textLines = paragraph.getTextLines();
78   // case4: 获取排版后指定行对应的度量信息
79   let lineCnt = paragraph.getLineCount();
80   for (let index = 0; index < lineCnt; index++) {
81       let lineMetrics = paragraph.getLineMetrics(index);
82   }
83   // case5: 获取排版后所有行度量信息数组
84   let allLineMetrics = paragraph.getLineMetrics();
85   ```
86
87## 完整示例
88
89完整的文本测量示例如下。
90
91```ts
92// Index.ets
93import { NodeController, FrameNode, RenderNode, DrawContext } from '@kit.ArkUI'
94import { UIContext } from '@kit.ArkUI'
95import { text } from '@kit.ArkGraphics2D'
96
97class MyRenderNode extends RenderNode {
98  async draw(context: DrawContext) {
99    // 获取画布canvas对象
100    const canvas = context.canvas
101    // 设置文本样式
102    let myTextStyle: text.TextStyle = {
103      color: {
104        alpha: 255,
105        red: 255,
106        green: 0,
107        blue: 0
108      },
109      fontSize: 100
110    };
111    // 创建一个段落样式对象,以设置排版风格
112    let myParagraphStyle: text.ParagraphStyle = {
113      textStyle: myTextStyle,
114      wordBreak: text.WordBreak.NORMAL
115    }
116    // 创建一个段落生成器
117    let paragraphBuilder = new text.ParagraphBuilder(myParagraphStyle, new text.FontCollection())
118    // 在段落生成器中设置文本样式
119    paragraphBuilder.pushStyle(myTextStyle);
120    // 在段落生成器中设置文本内容
121    paragraphBuilder.addText("文本测量测试");
122    // 通过段落生成器生成段落
123    let paragraph = paragraphBuilder.build();
124    // 布局
125    paragraph.layoutSync(1000);
126    // case1: 获取排版后最长行行宽
127    let longestLineWidth = paragraph.getLongestLine();
128    console.info("longestLineWidth = " + longestLineWidth);
129
130    // case2: 获取排版后最长行行宽(包含缩进)
131    let longestLineWithIndentWidth = paragraph.getLongestLineWithIndent();
132    console.info("longestLineWithIndentWidth = " + longestLineWithIndentWidth);
133
134    // case3: 获取排版后所有行对象
135    let textLines = paragraph.getTextLines();
136    for (let index = 0; index < textLines.length; index++) {
137      const textline = textLines[index];
138      let curLineRange = textline.getTextRange();
139      let curLineGlyCnt = textline.getGlyphCount();
140      console.info("第" + index + "行 TextRange start: " + curLineRange.start + " TextRange end: " + curLineRange.end);
141      console.info("第" + index + "行字形数量为: " + curLineGlyCnt);
142    }
143
144    // case4: 获取排版后指定行对应的度量信息
145    let lineCnt = paragraph.getLineCount();
146    for (let index = 0; index < lineCnt; index++) {
147      let lineMetrics = paragraph.getLineMetrics(index);
148      if (lineMetrics) {
149        console.info("第" + index + "行 lineMetrics width: " + lineMetrics.width);
150        console.info("第" + index + "行 lineMetrics start index: " + lineMetrics.startIndex + ", end index: " +
151        lineMetrics.endIndex);
152      }
153    }
154
155    // case5: 获取排版后所有行度量信息数组
156    let allLineMetrics = paragraph.getLineMetrics();
157    paragraph.paint(canvas, 200, 800);
158  }
159}
160
161// 创建一个MyRenderNode对象
162function getNewRenderNode() {
163  const textNodeTest = new MyRenderNode();
164  textNodeTest.frame = {
165    x: 0,
166    y: 0,
167    width: 500,
168    height: 500
169  }
170  textNodeTest.pivot = { x: 0.5, y: 0.5 }
171  textNodeTest.scale = { x: 1, y: 1 }
172  return textNodeTest;
173}
174
175const textNode = new MyRenderNode();
176// 定义newNode的像素格式
177textNode.frame = {
178  x: 0,
179  y: 0,
180  width: 500,
181  height: 500
182}
183textNode.pivot = { x: 0.5, y: 0.5 }
184textNode.scale = { x: 1, y: 1 }
185
186class MyNodeController extends NodeController {
187  private rootNode: FrameNode | null = null;
188
189  makeNode(uiContext: UIContext): FrameNode {
190    this.rootNode = new FrameNode(uiContext)
191    if (this.rootNode == null) {
192      return this.rootNode
193    }
194    const renderNode = this.rootNode.getRenderNode()
195    if (renderNode != null) {
196      renderNode.frame = {
197        x: 0,
198        y: 0,
199        width: 300,
200        height: 50
201      }
202      renderNode.pivot = { x: 0, y: 0 }
203    }
204    return this.rootNode
205  }
206
207  addNode(node: RenderNode): void {
208    if (this.rootNode == null) {
209      return
210    }
211    const renderNode = this.rootNode.getRenderNode()
212    if (renderNode != null) {
213      renderNode.appendChild(node)
214    }
215  }
216
217  clearNodes(): void {
218    if (this.rootNode == null) {
219      return
220    }
221    const renderNode = this.rootNode.getRenderNode()
222    if (renderNode != null) {
223      renderNode.clearChildren()
224    }
225  }
226}
227
228@Entry
229@Component
230struct RenderTest {
231  private myNodeController: MyNodeController = new MyNodeController()
232
233  build() {
234    Column() {
235      Row() {
236        NodeContainer(this.myNodeController)
237          .height('100%')
238      }
239      .height('90%')
240      .backgroundColor(Color.White)
241
242      Row() {
243        Button("Draw Text")
244          .fontSize('16fp')
245          .fontWeight(500)
246          .margin({ bottom: 24, right: 12 })
247          .onClick(() => {
248            this.myNodeController.clearNodes()
249            this.myNodeController.addNode(getNewRenderNode())
250          })
251          .width('50%')
252          .height(40)
253      }
254      .width('100%')
255      .justifyContent(FlexAlign.Center)
256      .alignItems(VerticalAlign.Bottom)
257      .layoutWeight(1)
258    }
259  }
260}
261```
262