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