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<TextLine> | 获取当前段落文本行对象数组。 | 34| getLineMetrics(): Array<LineMetrics> | 获取段落所有行的度量信息。包含行的高度、宽度、起始坐标等信息。 | 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