1# 属性字符串(StyledString/MutableStyledString) 2<!--Kit: ArkUI--> 3<!--Subsystem: ArkUI--> 4<!--Owner: @hddgzw--> 5<!--Designer: @pssea--> 6<!--Tester: @jiaoaozihao--> 7<!--Adviser: @HelloCrease--> 8 9属性字符串StyledString/MutableStyledString(其中MutableStyledString继承自StyledString,下文统称为StyledString),可用于在字符或段落级别上设置文本样式。将StyledString应用到文本组件上,可以采用多种方式修改文本,包括调整字号、添加字体颜色、使文本具备可点击性,以及通过自定义方式绘制文本等。具体使用方法请参考[属性字符串](../reference/apis-arkui/arkui-ts/ts-universal-styled-string.md)的文档。 10 11属性字符串提供多种类型样式对象,涵盖各种常见的文本样式格式,例如文本装饰线样式、文本行高样式、文本阴影样式等。也可以自行创建CustomSpan,以应用自定义样式。 12 13## 创建并应用StyledString和MutableStyledString 14 15 可以通过TextController提供的[setStyledString](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#setstyledstring12)方法,将属性字符串附加到文本组件,并推荐在[onPageShow](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#onpageshow)或者文本组件的[onAppear](../reference/apis-arkui/arkui-ts/ts-universal-events-show-hide.md#onappear)回调中触发绑定。 16 > **说明:** 17 > 18 > 在aboutToAppear中调用setStyledString方法时,由于该方法运行阶段组件尚未完成创建并成功挂载节点树,因此无法在页面初始化时显示属性字符串。 19 > 20 > 从API version 15开始,在aboutToAppear中调用setStyledString方法,页面初始化时可以显示属性字符串。 21 22 ```ts 23 @Entry 24 @Component 25 struct styled_string_demo1 { 26 styledString1: StyledString = new StyledString("运动45分钟"); 27 mutableStyledString1: MutableStyledString = new MutableStyledString("运动35分钟"); 28 controller1: TextController = new TextController(); 29 controller2: TextController = new TextController(); 30 31 async onPageShow() { 32 // 在生命周期onPageShow回调中绑定属性字符串 33 this.controller1.setStyledString(this.styledString1); 34 } 35 36 build() { 37 Column() { 38 // 显示属性字符串 39 Text(undefined, { controller: this.controller1 }) 40 Text(undefined, { controller: this.controller2 }) 41 .onAppear(() => { 42 // 在组件onAppear回调中绑定属性字符串 43 this.controller2.setStyledString(this.mutableStyledString1); 44 }) 45 } 46 .width('100%') 47 } 48 } 49 ``` 50  51 52## 设置文本样式 53 54属性字符串目前提供了多种Style对象,包括[TextStyle](../reference/apis-arkui/arkui-ts/ts-universal-styled-string.md#textstyle)、[TextShadowStyle](../reference/apis-arkui/arkui-ts/ts-universal-styled-string.md#textshadowstyle)、[DecorationStyle](../reference/apis-arkui/arkui-ts/ts-universal-styled-string.md#decorationstyle)、[BaselineOffsetStyle](../reference/apis-arkui/arkui-ts/ts-universal-styled-string.md#baselineoffsetstyle)、[LineHeightStyle](../reference/apis-arkui/arkui-ts/ts-universal-styled-string.md#lineheightstyle)、[LetterSpacingStyle](../reference/apis-arkui/arkui-ts/ts-universal-styled-string.md#letterspacingstyle),用于设置文本的各类样式。 55 56- 创建及应用文本字体样式对象(TextStyle) 57 58 ```ts 59 import { LengthMetrics } from '@kit.ArkUI'; 60 61 @Entry 62 @Component 63 struct styled_string_demo2 { 64 textStyleAttrs: TextStyle = 65 new TextStyle({ fontWeight: FontWeight.Bolder, fontSize: LengthMetrics.vp(24), fontStyle: FontStyle.Italic, strokeWidth: LengthMetrics.px(5), strokeColor: Color.Green }); 66 mutableStyledString: MutableStyledString = new MutableStyledString("运动45分钟 目标达成", [ 67 { 68 start: 2, 69 length: 2, 70 styledKey: StyledStringKey.FONT, 71 styledValue: this.textStyleAttrs 72 }, 73 { 74 start: 7, 75 length: 4, 76 styledKey: StyledStringKey.FONT, 77 styledValue: new TextStyle({ fontColor: Color.Orange, fontSize: LengthMetrics.vp(12), 78 superscript: SuperscriptStyle.SUPERSCRIPT }) 79 } 80 ]); 81 controller: TextController = new TextController(); 82 83 async onPageShow() { 84 this.controller.setStyledString(this.mutableStyledString); 85 } 86 87 build() { 88 Column() { 89 // 显示属性字符串 90 Text(undefined, { controller: this.controller }) 91 .margin({ top: 10 }) 92 } 93 .width('100%') 94 } 95 } 96 ``` 97  98 99- 创建及应用文本阴影对象(TextShadowStyle) 100 101 ```ts 102 // xxx.ets 103 @Entry 104 @Component 105 struct styled_string_demo3 { 106 mutableStyledString: MutableStyledString = new MutableStyledString("运动35分钟", [ 107 { 108 start: 0, 109 length: 3, 110 styledKey: StyledStringKey.TEXT_SHADOW, 111 styledValue: new TextShadowStyle({ 112 radius: 5, 113 type: ShadowType.COLOR, 114 color: Color.Red, 115 offsetX: 10, 116 offsetY: 10 117 }) 118 } 119 ]); 120 controller: TextController = new TextController(); 121 122 async onPageShow() { 123 this.controller.setStyledString(this.mutableStyledString); 124 } 125 126 build() { 127 Column() { 128 // 显示属性字符串 129 Text(undefined, { controller: this.controller }) 130 } 131 .width('100%') 132 } 133 } 134 ``` 135  136 137- 创建及应用文本装饰线对象(DecorationStyle) 138 139 ```ts 140 // xxx.ets 141 @Entry 142 @Component 143 struct styled_string_demo4 { 144 mutableStyledString: MutableStyledString = new MutableStyledString("运动35分钟", [ 145 { 146 start: 0, 147 length: 4, 148 styledKey: StyledStringKey.DECORATION, 149 styledValue: new DecorationStyle({ type: TextDecorationType.LineThrough, color: Color.Red, thicknessScale: 3 }) 150 }, 151 { 152 start: 4, 153 length: 2, 154 styledKey: StyledStringKey.DECORATION, 155 styledValue: new DecorationStyle( 156 { 157 type: TextDecorationType.Underline, 158 }, 159 { 160 // 开启多装饰线 161 enableMultiType: true 162 } 163 ) 164 }, 165 { 166 start: 4, 167 length: 2, 168 styledKey: StyledStringKey.DECORATION, 169 styledValue: new DecorationStyle( 170 { 171 type: TextDecorationType.LineThrough, 172 }, 173 { 174 // 开启多装饰线 175 enableMultiType: true 176 } 177 ) 178 }, 179 ]); 180 controller: TextController = new TextController(); 181 182 async onPageShow() { 183 this.controller.setStyledString(this.mutableStyledString); 184 } 185 186 build() { 187 Column() { 188 // 显示属性字符串 189 Text(undefined, { controller: this.controller }) 190 } 191 .width('100%') 192 } 193 } 194 ``` 195  196 197- 创建及应用文本基线偏移量对象(BaselineOffsetStyle) 198 199 ```ts 200 import { LengthMetrics } from '@kit.ArkUI'; 201 202 // xxx.ets 203 @Entry 204 @Component 205 struct styled_string_demo5 { 206 mutableStyledString: MutableStyledString = new MutableStyledString("运动35分钟", [ 207 { 208 start: 0, 209 length: 3, 210 styledKey: StyledStringKey.BASELINE_OFFSET, 211 styledValue: new BaselineOffsetStyle(LengthMetrics.px(20)) 212 } 213 ]); 214 controller: TextController = new TextController(); 215 216 async onPageShow() { 217 this.controller.setStyledString(this.mutableStyledString); 218 } 219 220 build() { 221 Column() { 222 // 显示属性字符串 223 Text(undefined, { controller: this.controller }) 224 } 225 .width('100%') 226 } 227 } 228 ``` 229  230 231- 创建及应用文本行高对象(LineHeightStyle) 232 233 ```ts 234 import { LengthMetrics } from '@kit.ArkUI'; 235 236 // xxx.ets 237 @Entry 238 @Component 239 struct styled_string_demo6 { 240 mutableStyledString: MutableStyledString = new MutableStyledString("运动35分钟\n顶顶顶\n得到", [ 241 { 242 start: 8, 243 length: 3, 244 styledKey: StyledStringKey.LINE_HEIGHT, 245 styledValue: new LineHeightStyle(LengthMetrics.vp(50)) 246 } 247 ]); 248 controller: TextController = new TextController(); 249 250 async onPageShow() { 251 this.controller.setStyledString(this.mutableStyledString); 252 } 253 254 build() { 255 Column() { 256 // 显示属性字符串 257 Text(undefined, { controller: this.controller }) 258 } 259 .width('100%') 260 .margin({ top: 10 }) 261 } 262 } 263 ``` 264  265 266- 创建及应用文本字符间距对象(LetterSpacingStyle) 267 268 ```ts 269 import { LengthMetrics, LengthUnit } from '@kit.ArkUI'; 270 271 // xxx.ets 272 @Entry 273 @Component 274 struct styled_string_demo7 { 275 mutableStyledString: MutableStyledString = new MutableStyledString("运动35分钟", [ 276 { 277 start: 0, 278 length: 2, 279 styledKey: StyledStringKey.LETTER_SPACING, 280 styledValue: new LetterSpacingStyle(new LengthMetrics(20, LengthUnit.VP)) 281 } 282 ]); 283 controller: TextController = new TextController(); 284 285 async onPageShow() { 286 this.controller.setStyledString(this.mutableStyledString); 287 } 288 289 build() { 290 Column() { 291 // 显示属性字符串 292 Text(undefined, { controller: this.controller }) 293 } 294 .width('100%') 295 } 296 } 297 ``` 298  299 300## 设置段落样式 301 302可通过[ParagraphStyle](../reference/apis-arkui/arkui-ts/ts-universal-styled-string.md#paragraphstyle)设置段落样式布局。下图显示了如何分割文本中的段落,段落以换行符 \n 结尾。 303 304 305 306以下代码示例展示了如何创建ParagraphStyle并应用。如果将ParagraphStyle附加到段落开头、末尾或之间的任何位置,均会应用样式,非段落区间内则不会应用样式。 307 308 ```ts 309 import { LengthMetrics} from '@kit.ArkUI'; 310 311 // xxx.ets 312 @Entry 313 @Component 314 struct Index { 315 titleParagraphStyleAttr: ParagraphStyle = new ParagraphStyle({ textAlign: TextAlign.Center }); 316 // 段落首行缩进15vp 317 paragraphStyleAttr1: ParagraphStyle = new ParagraphStyle({ textIndent: LengthMetrics.vp(15) }); 318 // 行高样式对象 319 lineHeightStyle1: LineHeightStyle = new LineHeightStyle(new LengthMetrics(24)); 320 // 创建含段落样式的对象paragraphStyledString1 321 paragraphStyledString1: MutableStyledString = 322 new MutableStyledString("段落标题\n正文第一段落开始0123456789正文第一段落结束。", [ 323 { 324 start: 0, 325 length: 4, 326 styledKey: StyledStringKey.PARAGRAPH_STYLE, 327 styledValue: this.titleParagraphStyleAttr 328 }, 329 { 330 start: 0, 331 length: 4, 332 styledKey: StyledStringKey.LINE_HEIGHT, 333 styledValue: new LineHeightStyle(new LengthMetrics(50)) 334 }, { 335 start: 0, 336 length: 4, 337 styledKey: StyledStringKey.FONT, 338 styledValue: new TextStyle({ fontSize: LengthMetrics.vp(24), fontWeight: FontWeight.Bolder }) 339 }, 340 { 341 start: 5, 342 length: 3, 343 styledKey: StyledStringKey.PARAGRAPH_STYLE, 344 styledValue: this.paragraphStyleAttr1 345 }, 346 { 347 start: 5, 348 length: 20, 349 styledKey: StyledStringKey.LINE_HEIGHT, 350 styledValue: this.lineHeightStyle1 351 } 352 ]); 353 controller: TextController = new TextController(); 354 355 async onPageShow() { 356 this.controller.setStyledString(this.paragraphStyledString1); 357 } 358 359 build() { 360 Column() { 361 // 显示属性字符串 362 Text(undefined, { controller: this.controller }) 363 } 364 .width('100%') 365 } 366 } 367 ``` 368 369  370 371 除了可以在创建属性字符串时就预设样式,也可以后续通过[replaceStyle](../reference/apis-arkui/arkui-ts/ts-universal-styled-string.md#replacestyle)清空原样式替换新样式,同时需要在附加的文本组件controller上主动触发更新绑定的属性字符串。 372 373 ```ts 374 import { LengthMetrics } from '@kit.ArkUI'; 375 376 // xxx.ets 377 @Entry 378 @Component 379 struct Index { 380 titleParagraphStyleAttr: ParagraphStyle = new ParagraphStyle({ textAlign: TextAlign.Center }); 381 // 段落首行缩进15vp 382 paragraphStyleAttr1: ParagraphStyle = new ParagraphStyle({ textIndent: LengthMetrics.vp(15) }); 383 // 行高样式对象 384 lineHeightStyle1: LineHeightStyle = new LineHeightStyle(new LengthMetrics(24)); 385 // 创建含段落样式的对象paragraphStyledString1 386 paragraphStyledString1: MutableStyledString = 387 new MutableStyledString("段落标题\n正文第一段落开始0123456789正文第一段落结束,通过replaceStyle清空原样式替换新样式。", [ 388 { 389 start: 0, 390 length: 4, 391 styledKey: StyledStringKey.PARAGRAPH_STYLE, 392 styledValue: this.titleParagraphStyleAttr 393 }, 394 { 395 start: 0, 396 length: 4, 397 styledKey: StyledStringKey.LINE_HEIGHT, 398 styledValue: new LineHeightStyle(new LengthMetrics(50)) 399 }, { 400 start: 0, 401 length: 4, 402 styledKey: StyledStringKey.FONT, 403 styledValue: new TextStyle({ fontSize: LengthMetrics.vp(24), fontWeight: FontWeight.Bolder }) 404 }, 405 { 406 start: 5, 407 length: 3, 408 styledKey: StyledStringKey.PARAGRAPH_STYLE, 409 styledValue: this.paragraphStyleAttr1 410 }, 411 { 412 start: 5, 413 length: 20, 414 styledKey: StyledStringKey.LINE_HEIGHT, 415 styledValue: this.lineHeightStyle1 416 } 417 ]); 418 paragraphStyleAttr3: ParagraphStyle = new ParagraphStyle({ 419 textAlign: TextAlign.End, 420 maxLines: 1, 421 wordBreak: WordBreak.BREAK_ALL, 422 overflow: TextOverflow.Ellipsis 423 }); 424 controller: TextController = new TextController(); 425 426 async onPageShow() { 427 this.controller.setStyledString(this.paragraphStyledString1); 428 } 429 430 build() { 431 Column() { 432 // 显示属性字符串 433 Text(undefined, { controller: this.controller }).width(300) 434 Button('替换段落样式') 435 .onClick(() => { 436 this.paragraphStyledString1.replaceStyle({ 437 start: 5, 438 length: 3, 439 styledKey: StyledStringKey.PARAGRAPH_STYLE, 440 styledValue: this.paragraphStyleAttr3 441 }); 442 this.controller.setStyledString(this.paragraphStyledString1); 443 }) 444 } 445 .width('100%') 446 } 447 } 448 ``` 449 450  451 452## 支持将属性字符串转换成Paragraph 453 454可通过[getParagraphs](../reference/apis-arkui/arkts-apis-uicontext-measureutils.md#getparagraphs20)将属性字符串根据文本布局选项转换成对应的[Paragraph](../reference/apis-arkgraphics2d/js-apis-graphics-text.md#paragraph)数组。 455 456- 以下示例展示了通过MeasureUtils的getParagraphs方法测算文本,当内容超出最大显示行数的时候,截断文本显示并展示“...全文”的效果。 457 458 ```ts 459 import { LengthMetrics } from '@kit.ArkUI'; 460 import { drawing } from '@kit.ArkGraphics2D'; 461 462 class MyCustomSpan extends CustomSpan { 463 constructor(word: string, width: number, height: number, context: UIContext) { 464 super(); 465 this.word = word; 466 this.width = width; 467 this.height = height; 468 this.context = context; 469 } 470 471 onMeasure(measureInfo: CustomSpanMeasureInfo): CustomSpanMetrics { 472 return { width: this.width, height: this.height }; 473 } 474 475 onDraw(context: DrawContext, options: CustomSpanDrawInfo) { 476 let canvas = context.canvas; 477 const brush = new drawing.Brush(); 478 brush.setColor({ 479 alpha: 255, 480 red: 0, 481 green: 74, 482 blue: 175 483 }); 484 const font = new drawing.Font(); 485 font.setSize(25); 486 const textBlob = drawing.TextBlob.makeFromString(this.word, font, drawing.TextEncoding.TEXT_ENCODING_UTF8); 487 canvas.attachBrush(brush); 488 canvas.drawRect({ 489 left: options.x + 10, 490 right: options.x + this.context.vp2px(this.width) - 10, 491 top: options.lineTop + 10, 492 bottom: options.lineBottom - 10 493 }); 494 brush.setColor({ 495 alpha: 255, 496 red: 23, 497 green: 169, 498 blue: 141 499 }); 500 canvas.attachBrush(brush); 501 canvas.drawTextBlob(textBlob, options.x + 20, options.lineBottom - 15); 502 canvas.detachBrush(); 503 } 504 505 setWord(word: string) { 506 this.word = word; 507 } 508 509 width: number = 160; 510 word: string = "drawing"; 511 height: number = 10; 512 context: UIContext; 513 } 514 515 @Entry 516 @Component 517 struct Index { 518 str: string = 519 "Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal."; 520 mutableStr2 = new MutableStyledString(this.str, [ 521 { 522 start: 0, 523 length: 3, 524 styledKey: StyledStringKey.FONT, 525 styledValue: new TextStyle({ fontSize: LengthMetrics.px(20) }) 526 }, 527 { 528 start: 3, 529 length: 3, 530 styledKey: StyledStringKey.FONT, 531 styledValue: new TextStyle({ fontColor: Color.Brown }) 532 } 533 ]); 534 535 // 测算属性字符串在指定宽度下能显示的行数 536 getLineNum(styledString: StyledString, width: LengthMetrics) { 537 let paragraphArr = this.getUIContext().getMeasureUtils().getParagraphs(styledString, { constraintWidth: width }); 538 let res = 0; 539 for (let i = 0; i < paragraphArr.length; ++i) { 540 res += paragraphArr[i].getLineCount(); 541 } 542 return res; 543 } 544 545 // 测算属性字符串显示maxLines行时最多可以显示的字数 546 getCorrectIndex(styledString: MutableStyledString, maxLines: number, width: LengthMetrics) { 547 let low = 0; 548 let high = styledString.length - 1; 549 // 使用二分查找 550 while (low <= high) { 551 let mid = (low + high) >> 1; 552 console.log("demo: get " + low + " " + high + " " + mid); 553 let moreStyledString = new MutableStyledString("... 全文", [{ 554 start: 4, 555 length: 2, 556 styledKey: StyledStringKey.FONT, 557 styledValue: new TextStyle({ fontColor: Color.Blue }) 558 }]); 559 moreStyledString.insertStyledString(0, styledString.subStyledString(0, mid)); 560 let lineNum = this.getLineNum(moreStyledString, LengthMetrics.px(500)); 561 if (lineNum <= maxLines) { 562 low = mid + 1; 563 } else { 564 high = mid - 1; 565 } 566 } 567 return high; 568 } 569 570 mutableStrAllContent = new MutableStyledString(this.str, [ 571 { 572 start: 0, 573 length: 3, 574 styledKey: StyledStringKey.FONT, 575 styledValue: new TextStyle({ fontSize: LengthMetrics.px(40) }) 576 }, 577 { 578 start: 3, 579 length: 3, 580 styledKey: StyledStringKey.FONT, 581 styledValue: new TextStyle({ fontColor: Color.Brown }) 582 } 583 ]); 584 customSpan1: MyCustomSpan = new MyCustomSpan("Hello", 120, 10, this.getUIContext()); 585 mutableStrAllContent2 = new MutableStyledString(this.str, [ 586 { 587 start: 0, 588 length: 3, 589 styledKey: StyledStringKey.FONT, 590 styledValue: new TextStyle({ fontSize: LengthMetrics.px(100) }) 591 }, 592 { 593 start: 3, 594 length: 3, 595 styledKey: StyledStringKey.FONT, 596 styledValue: new TextStyle({ fontColor: Color.Brown }) 597 } 598 ]); 599 controller: TextController = new TextController(); 600 controller2: TextController = new TextController(); 601 textController: TextController = new TextController(); 602 textController2: TextController = new TextController(); 603 604 aboutToAppear() { 605 this.mutableStrAllContent2.insertStyledString(0, new StyledString(this.customSpan1)); 606 this.mutableStr2.insertStyledString(0, new StyledString(this.customSpan1)); 607 } 608 609 build() { 610 Scroll() { 611 Column() { 612 Text('原文') 613 Text(undefined, { controller: this.controller }).width('500px').onAppear(() => { 614 this.controller.setStyledString(this.mutableStrAllContent); 615 }) 616 Divider().strokeWidth(8).color('#F1F3F5') 617 Text('排版后') 618 Text(undefined, { controller: this.textController }).onAppear(() => { 619 let now = this.getCorrectIndex(this.mutableStrAllContent, 3, LengthMetrics.px(500)); 620 if (now != this.mutableStrAllContent.length - 1) { 621 let moreStyledString = new MutableStyledString("... 全文", [{ 622 start: 4, 623 length: 2, 624 styledKey: StyledStringKey.FONT, 625 styledValue: new TextStyle({ fontColor: Color.Blue }) 626 }]); 627 moreStyledString.insertStyledString(0, this.mutableStrAllContent.subStyledString(0, now)); 628 this.textController.setStyledString(moreStyledString); 629 } else { 630 this.textController.setStyledString(this.mutableStrAllContent); 631 } 632 }) 633 .width('500px') 634 Divider().strokeWidth(8).color('#F1F3F5') 635 Text('原文') 636 Text(undefined, { controller: this.controller2 }).width('500px').onAppear(() => { 637 this.controller2.setStyledString(this.mutableStrAllContent2); 638 }) 639 Divider().strokeWidth(8).color('#F1F3F5') 640 Text('排版后') 641 Text(undefined, { controller: this.textController2 }).onAppear(() => { 642 let now = this.getCorrectIndex(this.mutableStrAllContent2, 3, LengthMetrics.px(500)); 643 let moreStyledString = new MutableStyledString("... 全文", [{ 644 start: 4, 645 length: 2, 646 styledKey: StyledStringKey.FONT, 647 styledValue: new TextStyle({ fontColor: Color.Blue }) 648 }]); 649 moreStyledString.insertStyledString(0, this.mutableStrAllContent2.subStyledString(0, now)); 650 this.textController2.setStyledString(moreStyledString); 651 }) 652 .width('500px') 653 }.width('100%') 654 } 655 } 656 } 657 ``` 658 659  660 661 662## 使用图片 663 664可通过[ImageAttachment](../reference/apis-arkui/arkui-ts/ts-universal-styled-string.md#imageattachment)来添加图片。 665 666以下示例展示了如何将图片和文本附加到同一个MutableStyledString对象上,并实现图文混排。 667 668 ```ts 669 // xxx.ets 670 import { image } from '@kit.ImageKit'; 671 import { LengthMetrics } from '@kit.ArkUI'; 672 673 @Entry 674 @Component 675 struct styled_string_demo4 { 676 @State message: string = 'Hello World'; 677 imagePixelMap: image.PixelMap | undefined = undefined; 678 @State imagePixelMap3: image.PixelMap | undefined = undefined; 679 mutableStr: MutableStyledString = new MutableStyledString('123'); 680 controller: TextController = new TextController(); 681 mutableStr2: MutableStyledString = new MutableStyledString('This is set decoration line style to the mutableStr2', [{ 682 start: 0, 683 length: 15, 684 styledKey: StyledStringKey.DECORATION, 685 styledValue: new DecorationStyle({ 686 type: TextDecorationType.Overline, 687 color: Color.Orange, 688 style: TextDecorationStyle.DOUBLE 689 }) 690 }]); 691 692 async aboutToAppear() { 693 console.info("aboutToAppear initial imagePixelMap"); 694 // $r('app.media.sea')需要替换为开发者所需的图像资源文件。 695 this.imagePixelMap = await this.getPixmapFromMedia($r('app.media.sea')); 696 } 697 698 private async getPixmapFromMedia(resource: Resource) { 699 let unit8Array = await this.getUIContext().getHostContext()?.resourceManager?.getMediaContent(resource.id); 700 let imageSource = image.createImageSource(unit8Array?.buffer?.slice(0, unit8Array?.buffer?.byteLength)); 701 let createPixelMap: image.PixelMap = await imageSource.createPixelMap({ 702 desiredPixelFormat: image.PixelMapFormat.RGBA_8888 703 }); 704 await imageSource.release(); 705 return createPixelMap; 706 } 707 708 leadingMarginValue: ParagraphStyle = new ParagraphStyle({ leadingMargin: LengthMetrics.vp(5)}); 709 // 行高样式对象 710 lineHeightStyle1: LineHeightStyle= new LineHeightStyle(new LengthMetrics(24)); 711 // Bold样式 712 boldTextStyle: TextStyle = new TextStyle({ fontWeight: FontWeight.Bold }); 713 // 创建含段落样式的对象paragraphStyledString1 714 paragraphStyledString1: MutableStyledString = new MutableStyledString("\n品牌相纸 高清冲印30张\n限时直降5.15元 限量增送", [ 715 { 716 start: 0, 717 length: 28, 718 styledKey: StyledStringKey.PARAGRAPH_STYLE, 719 styledValue: this.leadingMarginValue 720 }, 721 { 722 start: 14, 723 length: 9, 724 styledKey: StyledStringKey.FONT, 725 styledValue: new TextStyle({ fontSize: LengthMetrics.vp(14), fontColor: '#B22222' }) 726 }, 727 { 728 start: 24, 729 length: 4, 730 styledKey: StyledStringKey.FONT, 731 styledValue: new TextStyle({ fontSize: LengthMetrics.vp(14), fontWeight: FontWeight.Lighter }) 732 }, 733 { 734 start: 11, 735 length: 4, 736 styledKey: StyledStringKey.LINE_HEIGHT, 737 styledValue: this.lineHeightStyle1 738 } 739 ]); 740 paragraphStyledString2: MutableStyledString = new MutableStyledString("\n¥16.21 3000+人好评", [ 741 { 742 start: 0, 743 length: 5, 744 styledKey: StyledStringKey.PARAGRAPH_STYLE, 745 styledValue: this.leadingMarginValue 746 }, 747 { 748 start: 0, 749 length: 4, 750 styledKey: StyledStringKey.LINE_HEIGHT, 751 styledValue: new LineHeightStyle(new LengthMetrics(60)) 752 }, 753 { 754 start: 0, 755 length: 7, 756 styledKey: StyledStringKey.FONT, 757 styledValue: this.boldTextStyle 758 }, 759 { 760 start: 1, 761 length: 1, 762 styledKey: StyledStringKey.FONT, 763 styledValue: new TextStyle({ fontSize: LengthMetrics.vp(18) }) 764 }, 765 { 766 start: 2, 767 length: 2, 768 styledKey: StyledStringKey.FONT, 769 styledValue: new TextStyle({ fontSize: LengthMetrics.vp(36) }) 770 }, 771 { 772 start: 4, 773 length: 3, 774 styledKey: StyledStringKey.FONT, 775 styledValue: new TextStyle({ fontSize: LengthMetrics.vp(20) }) 776 }, 777 { 778 start: 7, 779 length: 9, 780 styledKey: StyledStringKey.FONT, 781 styledValue: new TextStyle({ fontColor: Color.Grey, fontSize: LengthMetrics.vp(14)}) 782 } 783 ]); 784 785 build() { 786 Row() { 787 Column({ space: 10 }) { 788 Text(undefined, { controller: this.controller }) 789 .copyOption(CopyOptions.InApp) 790 .draggable(true) 791 .backgroundColor('#FFFFFF') 792 .borderRadius(5) 793 794 Button('点击查看商品卡片') 795 .onClick(() => { 796 if (this.imagePixelMap !== undefined) { 797 this.mutableStr = new MutableStyledString(new ImageAttachment({ 798 value: this.imagePixelMap, 799 size: { width: 180, height: 160 }, 800 verticalAlign: ImageSpanAlignment.BASELINE, 801 objectFit: ImageFit.Fill 802 })); 803 this.paragraphStyledString1.appendStyledString(this.paragraphStyledString2); 804 this.mutableStr.appendStyledString(this.paragraphStyledString1); 805 this.controller.setStyledString(this.mutableStr); 806 } 807 }) 808 } 809 .width('100%') 810 } 811 .height('100%') 812 .backgroundColor('#F8F8FF') 813 } 814 } 815 ``` 816  817 818## 设置事件 819 820可通过[GestureStyle](../reference/apis-arkui/arkui-ts/ts-universal-styled-string.md#gesturestyle)设置onClick、onLongPress事件来使文本响应点击长按事件。 821 822除了初始化属性字符串对象即初始样式对象,亦可通过[setStyle](../reference/apis-arkui/arkui-ts/ts-universal-styled-string.md#setstyle)接口再叠加新样式或更新已有样式,同时需要在附加的文本组件controller上主动触发更新绑定的属性字符串。 823 824 ```ts 825 import { drawing } from '@kit.ArkGraphics2D'; 826 827 let gUIContext: UIContext; 828 829 class MyCustomSpan extends CustomSpan { 830 constructor(word: string, width: number, height: number, fontSize: number) { 831 super(); 832 this.word = word; 833 this.width = width; 834 this.height = height; 835 this.fontSize = fontSize; 836 } 837 838 onMeasure(measureInfo: CustomSpanMeasureInfo): CustomSpanMetrics { 839 return { width: this.width, height: this.height }; 840 } 841 842 onDraw(context: DrawContext, options: CustomSpanDrawInfo) { 843 let canvas = context.canvas; 844 845 const brush = new drawing.Brush(); 846 brush.setColor({ 847 alpha: 255, 848 red: 0, 849 green: 0, 850 blue: 0 851 }); 852 const font = new drawing.Font(); 853 font.setSize(gUIContext.vp2px(this.fontSize)); 854 const textBlob = 855 drawing.TextBlob.makeFromString(this.word.substring(0, 5), font, drawing.TextEncoding.TEXT_ENCODING_UTF8); 856 canvas.attachBrush(brush); 857 858 this.onDrawRectByRadius(context, options.x, options.x + gUIContext.vp2px(this.width), options.lineTop, 859 options.lineBottom, 20); 860 brush.setColor({ 861 alpha: 255, 862 red: 255, 863 green: 255, 864 blue: 255 865 }); 866 canvas.attachBrush(brush); 867 canvas.drawTextBlob(textBlob, options.x, options.baseline); 868 brush.setColor({ 869 alpha: 255, 870 red: 255, 871 green: 228, 872 blue: 196 873 }); 874 canvas.attachBrush(brush); 875 const textBlob1 = 876 drawing.TextBlob.makeFromString(this.word.substring(5), font, drawing.TextEncoding.TEXT_ENCODING_UTF8); 877 canvas.drawTextBlob(textBlob1, options.x + gUIContext.vp2px(100), options.baseline); 878 879 canvas.detachBrush(); 880 } 881 882 onDrawRectByRadius(context: DrawContext, left: number, right: number, top: number, bottom: number, radius: number) { 883 let canvas = context.canvas; 884 let path = new drawing.Path(); 885 886 // 画带radius的rect 887 path.moveTo(left + radius, top); 888 path.lineTo(right - radius, top); 889 path.arcTo(right - 2 * radius, top, right, top + 2 * radius, 270, 90); 890 path.lineTo(right, bottom - radius); 891 path.arcTo(right - 2 * radius, bottom - 2 * radius, right, bottom, 0, 90); 892 893 path.lineTo(left + 2 * radius, bottom); 894 path.arcTo(left, bottom - 2 * radius, left + 2 * radius, bottom, 90, 90); 895 path.lineTo(left, top + 2 * radius); 896 path.arcTo(left, top, left + 2 * radius, top + 2 * radius, 180, 90); 897 898 canvas.drawPath(path); 899 } 900 901 setWord(word: string) { 902 this.word = word; 903 } 904 905 width: number = 160; 906 word: string = "drawing"; 907 height: number = 10; 908 fontSize: number = 16; 909 } 910 911 @Entry 912 @Component 913 struct styled_string_demo6 { 914 customSpan3: MyCustomSpan = new MyCustomSpan("99VIP88%off", 200, 40, 30); 915 customSpanStyledString: MutableStyledString = new MutableStyledString(this.customSpan3); 916 textController: TextController = new TextController(); 917 isPageShow: boolean = true; 918 @State backgroundColor1: ResourceColor | undefined = undefined; 919 gestureStyleAttr: GestureStyle = new GestureStyle({ 920 onClick: () => { 921 this.backgroundColor1 = Color.Green; 922 }, 923 onLongPress: () => { 924 this.backgroundColor1 = Color.Grey; 925 } 926 }); 927 928 aboutToAppear() { 929 gUIContext = this.getUIContext(); 930 } 931 932 async onPageShow() { 933 if (!this.isPageShow) { 934 return; 935 } 936 this.isPageShow = false; 937 this.customSpanStyledString.setStyle({ 938 start: 0, 939 length: 1, 940 styledKey: StyledStringKey.GESTURE, 941 styledValue: this.gestureStyleAttr 942 }) 943 this.textController.setStyledString(this.customSpanStyledString); 944 } 945 946 build() { 947 Row() { 948 Column() { 949 Button("响应属性字符串事件改变背景色").backgroundColor(this.backgroundColor1).width('80%').margin(10) 950 Text(undefined, { controller: this.textController }) 951 .copyOption(CopyOptions.InApp) 952 .fontSize(30) 953 } 954 .width('100%') 955 } 956 .height('100%') 957 } 958 } 959 ``` 960  961 962## 格式转换 963 964可以通过[toHtml](../reference/apis-arkui/arkui-ts/ts-universal-styled-string.md#tohtml14)、[fromHtml](../reference/apis-arkui/arkui-ts/ts-universal-styled-string.md#fromhtml)接口实现属性字符串与HTML格式字符串的相关转换,当前支持转换的HTML标签范围:\<p>、\<span>、\<img>、\<br>、\<strong>、\<b>、\<a>、\<i>、\<em>、\<s>、\<u>、\<del>、\<sup>、\<sub>。 965 966- 以下示例展示了如何将属性字符串转换成HTML格式,并展示了如何从HTML格式转换回属性字符串。 967```ts 968// xxx.ets 969import { image } from '@kit.ImageKit'; 970import { LengthMetrics } from '@kit.ArkUI'; 971 972@Entry 973@Component 974struct styled_string_demo8 { 975 imagePixelMap: image.PixelMap | undefined = undefined; 976 @State html: string | undefined = undefined; 977 @State styledString: StyledString | undefined = undefined; 978 controller1: TextController = new TextController; 979 controller2: TextController = new TextController; 980 private uiContext: UIContext = this.getUIContext(); 981 982 async aboutToAppear() { 983 console.info("aboutToAppear initial imagePixelMap"); 984 // $r('app.media.startIcon')需要替换为开发者所需的图像资源文件。 985 this.imagePixelMap = await this.getPixmapFromMedia($r('app.media.startIcon')); 986 } 987 988 private async getPixmapFromMedia(resource: Resource) { 989 let unit8Array = await this.uiContext.getHostContext()?.resourceManager?.getMediaContent(resource.id); 990 let imageSource = image.createImageSource(unit8Array?.buffer.slice(0, unit8Array.buffer.byteLength)); 991 let createPixelMap: image.PixelMap = await imageSource.createPixelMap({ 992 desiredPixelFormat: image.PixelMapFormat.RGBA_8888 993 }); 994 await imageSource.release(); 995 return createPixelMap; 996 } 997 998 build() { 999 Column() { 1000 Text(undefined, { controller: this.controller1 }).height(100) 1001 Row() { 1002 Button("添加属性字符串").onClick(() => { 1003 let mutableStyledString1: MutableStyledString = new MutableStyledString("属性字符串", [{ 1004 start: 0, 1005 length: 6, 1006 styledKey: StyledStringKey.FONT, 1007 styledValue: new TextStyle({ fontColor: Color.Green, fontSize: LengthMetrics.px(50) }) 1008 }]); 1009 if (this.imagePixelMap !== undefined) { 1010 let mutableStyledString2 = new MutableStyledString(new ImageAttachment({ 1011 value: this.imagePixelMap, 1012 size: { width: 50, height: 50 }, 1013 })); 1014 mutableStyledString1.appendStyledString(mutableStyledString2); 1015 } 1016 this.styledString = mutableStyledString1; 1017 this.controller1.setStyledString(mutableStyledString1); 1018 }).margin(5) 1019 Button("toHtml").onClick(() => { 1020 this.html = StyledString.toHtml(this.styledString); 1021 }).margin(5) 1022 Button("fromHtml").onClick(async () => { 1023 let styledString = await StyledString.fromHtml(this.html); 1024 this.controller2.setStyledString(styledString); 1025 }).margin(5) 1026 } 1027 1028 Text(undefined, { controller: this.controller2 }).height(100) 1029 Text(this.html) 1030 }.width("100%") 1031 } 1032} 1033``` 1034 1035 1036 1037- 将HTML中\<strong>、\<b>、\<a>、\<i>、\<em>、\<s>、\<u>、\<del>、\<sup>、\<sub>标签及其style属性中的background-color转换为属性字符串并转回HTML。 1038 ```ts 1039 // xxx.ets 1040 @Entry 1041 @Component 1042 struct HtmlSpanStringDemo { 1043 @State html: string = 1044 "<p>This is <b>b</b> <strong>strong</strong> <em>em</em> <i>i</i> <u>u</u> <del>del</del> <s>s</s> <span style = \"foreground-color:blue\"> <a href='https://www.example.com'>www.example</a> </span> <span style=\"background-color: red;\">red span</span> <sup>superscript</sup> and <sub>subscript</sub></p>"; 1045 @State spanString: StyledString | undefined = undefined; 1046 @State resultText: string = ""; // 保存结果文本的状态 1047 controller: TextController = new TextController; 1048 1049 build() { 1050 Column() { 1051 // 显示转换后的spanString 1052 Text(undefined, { controller: this.controller }).height(100) 1053 1054 // TextArea显示每个步骤的结果 1055 TextArea({ text: this.html }) 1056 .width("100%") 1057 .height(100) 1058 .margin(5) 1059 1060 // 按钮1:将HTML转换为SpanString 1061 Button("Converted HTML to SpanString").onClick(async () => { 1062 this.spanString = await StyledString.fromHtml(this.html); 1063 this.controller.setStyledString(this.spanString); 1064 this.resultText = "Converted HTML to SpanString successfully."; 1065 }).margin(5) 1066 1067 // 按钮2:将SpanString转换为HTML 1068 Button("Converted SpanString to HTML").onClick(() => { 1069 if (this.spanString) { 1070 // 将spanString转换为HTML并替换当前的HTML状态 1071 const newHtml = StyledString.toHtml(this.spanString); 1072 if (newHtml !== this.html) { // 通过检查内容是否已经相同来防止重复 1073 this.html = newHtml; 1074 } 1075 this.resultText = "Converted SpanString to HTML successfully."; 1076 } else { 1077 this.resultText = "SpanString is undefined."; 1078 } 1079 }).margin(5) 1080 1081 // 按钮3:将HTML转换回SpanString 1082 Button("Converted HTML back to SpanString").onClick(async () => { 1083 this.spanString = await StyledString.fromHtml(this.html); 1084 this.controller.setStyledString(this.spanString); 1085 this.resultText = "Converted HTML back to SpanString successfully."; 1086 }).margin(5) 1087 1088 // 重置:重置HTML和SpanString 1089 Button("Reset").onClick(() => { 1090 this.html = 1091 "<p>This is <b>b</b> <strong>strong</strong> <em>em</em> <i>i</i> <u>u</u> <del>del</del> <s>s</s> <span style = \"foreground-color:blue\"> <a href='https://www.example.com'>www.example</a> </span> <span style=\"background-color: red;\">red span</span> <sup>superscript</sup> and <sub>subscript</sub></p>"; 1092 this.spanString = undefined; 1093 this.controller.setStyledString(new StyledString("")); // 使用空的StyledString实例 1094 this.resultText = "Reset HTML and SpanString successfully."; 1095 }).margin(5) 1096 }.width("100%").padding(20) 1097 } 1098 } 1099 ``` 1100 1101  1102 1103## 场景示例 1104 1105该示例通过ParagraphStyle、LineHeightStyle、TextStyle对象展示了会员过期提示的效果。 1106 1107```ts 1108import { LengthMetrics } from '@kit.ArkUI'; 1109 1110@Entry 1111@Component 1112struct Index { 1113 alignCenterParagraphStyleAttr: ParagraphStyle = new ParagraphStyle({ textAlign: TextAlign.Center }); 1114 // 行高样式对象 1115 lineHeightStyle1: LineHeightStyle = new LineHeightStyle(LengthMetrics.vp(24)); 1116 // Bold样式 1117 boldTextStyle: TextStyle = new TextStyle({ fontWeight: FontWeight.Bold }); 1118 // 创建含段落样式的对象paragraphStyledString1 1119 paragraphStyledString1: MutableStyledString = 1120 new MutableStyledString("您的豪华钻石已过期1天\n续费可继续享受会员专属权益", [ 1121 { 1122 start: 0, 1123 length: 4, 1124 styledKey: StyledStringKey.PARAGRAPH_STYLE, 1125 styledValue: this.alignCenterParagraphStyleAttr 1126 }, 1127 { 1128 start: 0, 1129 length: 4, 1130 styledKey: StyledStringKey.LINE_HEIGHT, 1131 styledValue: new LineHeightStyle(LengthMetrics.vp(40)) 1132 }, 1133 { 1134 start: 11, 1135 length: 14, 1136 styledKey: StyledStringKey.FONT, 1137 styledValue: new TextStyle({ fontSize: LengthMetrics.vp(14), fontColor: Color.Grey }) 1138 }, 1139 { 1140 start: 11, 1141 length: 4, 1142 styledKey: StyledStringKey.PARAGRAPH_STYLE, 1143 styledValue: this.alignCenterParagraphStyleAttr 1144 }, 1145 { 1146 start: 11, 1147 length: 4, 1148 styledKey: StyledStringKey.LINE_HEIGHT, 1149 styledValue: this.lineHeightStyle1 1150 } 1151 ]); 1152 paragraphStyledString2: MutableStyledString = new MutableStyledString("\n¥4.88¥15", [ 1153 { 1154 start: 0, 1155 length: 4, 1156 styledKey: StyledStringKey.PARAGRAPH_STYLE, 1157 styledValue: this.alignCenterParagraphStyleAttr 1158 }, 1159 { 1160 start: 0, 1161 length: 4, 1162 styledKey: StyledStringKey.LINE_HEIGHT, 1163 styledValue: new LineHeightStyle(LengthMetrics.vp(60)) 1164 }, 1165 { 1166 start: 0, 1167 length: 6, 1168 styledKey: StyledStringKey.FONT, 1169 styledValue: this.boldTextStyle 1170 }, 1171 { 1172 start: 1, 1173 length: 1, 1174 styledKey: StyledStringKey.FONT, 1175 styledValue: new TextStyle({ fontSize: LengthMetrics.vp(18) }) 1176 }, 1177 { 1178 start: 2, 1179 length: 4, 1180 styledKey: StyledStringKey.FONT, 1181 styledValue: new TextStyle({ fontSize: LengthMetrics.vp(40) }) 1182 }, 1183 { 1184 start: 6, 1185 length: 3, 1186 styledKey: StyledStringKey.FONT, 1187 styledValue: new TextStyle({ fontColor: Color.Grey, fontSize: LengthMetrics.vp(14) }) 1188 }, 1189 { 1190 start: 6, 1191 length: 3, 1192 styledKey: StyledStringKey.DECORATION, 1193 styledValue: new DecorationStyle({ type: TextDecorationType.LineThrough, color: Color.Grey }) 1194 } 1195 ]); 1196 paragraphStyledString3: MutableStyledString = new MutableStyledString("\n02时06分后将失去该优惠", [ 1197 { 1198 start: 0, 1199 length: 4, 1200 styledKey: StyledStringKey.PARAGRAPH_STYLE, 1201 styledValue: this.alignCenterParagraphStyleAttr 1202 }, 1203 { 1204 start: 0, 1205 length: 4, 1206 styledKey: StyledStringKey.LINE_HEIGHT, 1207 styledValue: new LineHeightStyle(LengthMetrics.vp(30)) 1208 }, 1209 { 1210 start: 1, 1211 length: 2, 1212 styledKey: StyledStringKey.FONT, 1213 styledValue: new TextStyle({ fontColor: '#FFD700', fontWeight: FontWeight.Bold }) 1214 }, 1215 { 1216 start: 4, 1217 length: 2, 1218 styledKey: StyledStringKey.FONT, 1219 styledValue: new TextStyle({ fontColor: '#FFD700', fontWeight: FontWeight.Bold }) 1220 } 1221 ]); 1222 controller: TextController = new TextController(); 1223 1224 build() { 1225 Row() { 1226 Column({ space: 5 }) { 1227 Text(undefined, { controller: this.controller }) 1228 .width(240) 1229 .copyOption(CopyOptions.InApp) 1230 .draggable(true) 1231 .onAppear(() => { 1232 this.paragraphStyledString2.appendStyledString(this.paragraphStyledString3); 1233 this.paragraphStyledString1.appendStyledString(this.paragraphStyledString2); 1234 this.controller.setStyledString(this.paragraphStyledString1); 1235 }) 1236 1237 Button("限时4.88元 立即续费") 1238 .width(200) 1239 .fontColor(Color.White) 1240 .fontSize(18) 1241 .backgroundColor('#3CB371') 1242 .margin({ bottom: 10 }) 1243 } 1244 .borderWidth(1).borderColor('#FFDEAD') 1245 .margin({ left: 10 }) 1246 } 1247 .height('60%') 1248 } 1249} 1250``` 1251 1252