• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 文本显示 (Text/Span)
2<!--Kit: ArkUI-->
3<!--Subsystem: ArkUI-->
4<!--Owner: @xiangyuan6-->
5<!--Designer: @pssea-->
6<!--Tester: @jiaoaozihao-->
7<!--Adviser: @HelloCrease-->
8
9
10Text是文本组件,用于展示用户视图,如显示文章的文字内容。该组件支持绑定自定义文本选择菜单,用户可根据需要选择不同功能。此外,还可以扩展自定义菜单,丰富可用选项,进一步提升用户体验。Span则用于展示行内文本。
11
12具体用法请参考[Text](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md)和[Span](../reference/apis-arkui/arkui-ts/ts-basic-components-span.md)组件的使用说明。
13
14
15## 创建文本
16
17Text可通过以下两种方式来创建:
18
19
20- string字符串。
21
22  ```ts
23  Text('我是一段文本')
24  ```
25
26
27![zh-cn_image_0000001563060685](figures/zh-cn_image_0000001563060685.png)
28
29
30- 引用Resource资源。
31
32  资源引用类型可以通过$r创建Resource类型对象,文件位置为/resources/base/element/string.json,具体内容如下:
33
34  ```json
35  {
36    "string": [
37      {
38        "name": "module_desc",
39        "value": "模块描述"
40      }
41    ]
42  }
43  ```
44
45  ```ts
46  Text($r('app.string.module_desc'))
47    .baselineOffset(0)
48    .fontSize(30)
49    .border({ width: 1 })
50    .padding(10)
51    .width(300)
52  ```
53
54  ![zh-cn_image_0000001511580872](figures/zh-cn_image_0000001511580872.png)
55
56
57## 添加子组件
58
59[Span](../reference/apis-arkui/arkui-ts/ts-basic-components-span.md)只能作为[Text](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md)和[RichEditor](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md)组件的子组件显示文本内容。可以在一个Text内添加多个Span来显示一段信息,例如产品说明书、承诺书等。
60
61- 创建Span。
62
63  Span组件需嵌入在Text组件中才能显示,单独使用时不会显示任何内容。Text与Span同时配置文本内容时,Span内容将覆盖Text内容。
64
65
66  ```ts
67  Text('我是Text') {
68    Span('我是Span')
69  }
70  .padding(10)
71  .borderWidth(1)
72  ```
73
74  ![zh-cn_image_0000001562700441](figures/zh-cn_image_0000001562700441.png)
75
76- 设置文本装饰线及颜色。
77
78  通过[decoration](../reference/apis-arkui/arkui-ts/ts-basic-components-span.md#decoration)设置文本装饰线及颜色。
79
80
81  ```ts
82  Text() {
83    Span('我是Span1,').fontSize(16).fontColor(Color.Grey)
84      .decoration({ type: TextDecorationType.LineThrough, color: Color.Red })
85    Span('我是Span2').fontColor(Color.Blue).fontSize(16)
86      .fontStyle(FontStyle.Italic)
87      .decoration({ type: TextDecorationType.Underline, color: Color.Black })
88    Span(',我是Span3').fontSize(16).fontColor(Color.Grey)
89      .decoration({ type: TextDecorationType.Overline, color: Color.Green })
90  }
91  .borderWidth(1)
92  .padding(10)
93  ```
94
95  ![zh-cn_image_0000001562700437](figures/zh-cn_image_0000001562700437.png)
96
97- 通过[textCase](../reference/apis-arkui/arkui-ts/ts-basic-components-span.md#textcase)设置文字一直保持大写或者小写状态。
98
99  ```ts
100  Text() {
101    Span('I am Upper-span').fontSize(12)
102      .textCase(TextCase.UpperCase)
103  }
104  .borderWidth(1)
105  .padding(10)
106  ```
107
108  ![zh-cn_image_0000001562940525](figures/zh-cn_image_0000001562940525.png)
109
110- 添加事件。
111
112  由于Span组件无尺寸信息,仅支持添加点击事件[onClick](../reference/apis-arkui/arkui-ts/ts-universal-events-click.md#onclick)、悬浮事件[onHover](../reference/apis-arkui/arkui-ts/ts-universal-events-hover.md#onhover)。
113
114
115  ```ts
116  // xxx.ets
117  @Entry
118  @Component
119  struct Index {
120    @State textStr1: string = '';
121    @State textStr2: string = '';
122
123    build() {
124      Row() {
125        Column() {
126          Text() {
127            Span('I am Upper-span')
128              .textCase(TextCase.UpperCase)
129              .fontSize(30)
130              .onClick(() => {
131                console.info('Span onClick is triggering');
132                this.textStr1 = 'Span onClick is triggering';
133              })
134              .onHover(() => {
135                console.info('Span onHover is triggering');
136                this.textStr2 = 'Span onHover is triggering';
137              })
138          }
139
140          Text('onClick:' + this.textStr1)
141            .fontSize(20)
142          Text('onHover:' + this.textStr2)
143            .fontSize(20)
144        }.width('100%')
145      }
146      .height('100%')
147    }
148  }
149  ```
150
151  ![span_event](figures/span_event.gif)
152
153## 创建自定义文本样式
154
155- 通过[textAlign](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#textalign)属性设置文本对齐样式。
156
157  ```ts
158  Text('左对齐')
159    .width(300)
160    .textAlign(TextAlign.Start)
161    .border({ width: 1 })
162    .padding(10)
163  Text('中间对齐')
164    .width(300)
165    .textAlign(TextAlign.Center)
166    .border({ width: 1 })
167    .padding(10)
168  Text('右对齐')
169    .width(300)
170    .textAlign(TextAlign.End)
171    .border({ width: 1 })
172    .padding(10)
173  ```
174
175  ![zh-cn_image_0000001511421260](figures/zh-cn_image_0000001511421260.png)
176
177- 通过[textOverflow](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#textoverflow)属性控制文本超长处理,textOverflow需配合[maxLines](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#maxlines)一起使用(默认情况下文本自动折行)。从API version 18开始,文本超长时设置跑马灯的方式展示时,支持设置跑马灯的配置项,比如开关、步长、循环次数、方向等。
178
179  ```ts
180  Text('This is the setting of textOverflow to Clip text content This is the setting of textOverflow to None text content. This is the setting of textOverflow to Clip text content This is the setting of textOverflow to None text content.')
181    .width(250)
182    .textOverflow({ overflow: TextOverflow.None })
183    .maxLines(1)
184    .fontSize(12)
185    .border({ width: 1 })
186    .padding(10)
187  Text('我是超长文本,超出的部分显示省略号。I am an extra long text, with ellipses displayed for any excess.')
188    .width(250)
189    .textOverflow({ overflow: TextOverflow.Ellipsis })
190    .maxLines(1)
191    .fontSize(12)
192    .border({ width: 1 })
193    .padding(10)
194  Text('当文本溢出其尺寸时,文本将滚动显示。When the text overflows its dimensions, the text will scroll for displaying.')
195    .width(250)
196    .textOverflow({ overflow: TextOverflow.MARQUEE })
197    .maxLines(1)
198    .fontSize(12)
199    .border({ width: 1 })
200    .padding(10)
201  Text('当文本溢出其尺寸时,文本将滚动显示,支持设置跑马灯配置项。When the text overflows its dimensions, the text will scroll for displaying.')
202    .width(250)
203    .textOverflow({ overflow: TextOverflow.MARQUEE })
204    .maxLines(1)
205    .fontSize(12)
206    .border({ width: 1 })
207    .padding(10)
208    .marqueeOptions({
209      start: true,
210      fromStart: true,
211      step: 6,
212      loop: -1,
213      delay: 0,
214      fadeout: false,
215      marqueeStartPolicy: MarqueeStartPolicy.DEFAULT
216    })
217  ```
218
219  ![zh-cn_image_0000001563060701](figures/zh-cn_image_0000001563060701.gif)
220
221- 通过[lineHeight](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#lineheight)属性设置文本行高。
222
223  ```ts
224  Text('This is the text with the line height set. This is the text with the line height set.')
225    .width(300).fontSize(12).border({ width: 1 }).padding(10)
226  Text('This is the text with the line height set. This is the text with the line height set.')
227    .width(300).fontSize(12).border({ width: 1 }).padding(10)
228    .lineHeight(20)
229  ```
230
231  ![zh-cn_image_0000001511740480](figures/zh-cn_image_0000001511740480.png)
232
233- 通过[decoration](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#decoration)属性设置文本装饰线样式、颜色及其粗细。
234
235  ```ts
236  Text('This is the text')
237    .decoration({
238      type: TextDecorationType.LineThrough,
239      color: Color.Red
240    })
241    .borderWidth(1).padding(15).margin(5)
242  Text('This is the text')
243    .decoration({
244      type: TextDecorationType.Overline,
245      color: Color.Red
246    })
247    .borderWidth(1).padding(15).margin(5)
248  Text('This is the text')
249    .decoration({
250      type: TextDecorationType.Underline,
251      color: Color.Red
252    })
253    .borderWidth(1).padding(15).margin(5)
254  Text('This is the text')
255    .decoration({
256      type: TextDecorationType.Underline,
257      color: Color.Blue,
258      style: TextDecorationStyle.DASHED
259    })
260    .borderWidth(1).padding(15).margin(5)
261  Text('This is the text')
262    .decoration({
263      type: TextDecorationType.Underline,
264      color: Color.Blue,
265      style: TextDecorationStyle.DOTTED
266    })
267    .borderWidth(1).padding(15).margin(5)
268  Text('This is the text')
269    .decoration({
270      type: TextDecorationType.Underline,
271      color: Color.Blue,
272      style: TextDecorationStyle.DOUBLE
273    })
274    .borderWidth(1).padding(15).margin(5)
275  Text('This is the text')
276    .decoration({
277      type: TextDecorationType.Underline,
278      color: Color.Blue,
279      style: TextDecorationStyle.WAVY,
280      thicknessScale: 4
281    })
282    .borderWidth(1).padding(15).margin(5)
283  ```
284
285  ![Text_decoration](figures/Text_decoration.jpg)
286
287- 通过[baselineOffset](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#baselineoffset)属性设置文本基线的偏移量。
288
289  ```ts
290  Text('This is the text content with baselineOffset 0.')
291    .baselineOffset(0)
292    .fontSize(12)
293    .border({ width: 1 })
294    .padding(10)
295    .width('100%')
296    .margin(5)
297  Text('This is the text content with baselineOffset 30.')
298    .baselineOffset(30)
299    .fontSize(12)
300    .border({ width: 1 })
301    .padding(10)
302    .width('100%')
303    .margin(5)
304  Text('This is the text content with baselineOffset -20.')
305    .baselineOffset(-20)
306    .fontSize(12)
307    .border({ width: 1 })
308    .padding(10)
309    .width('100%')
310    .margin(5)
311  ```
312
313  ![zh-cn_image_0000001562820789](figures/zh-cn_image_0000001562820789.png)
314
315- 通过[letterSpacing](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#letterspacing)属性设置文本字符间距。
316
317  ```ts
318  Text('This is the text content with letterSpacing 0.')
319    .letterSpacing(0)
320    .fontSize(12)
321    .border({ width: 1 })
322    .padding(10)
323    .width('100%')
324    .margin(5)
325  Text('This is the text content with letterSpacing 3.')
326    .letterSpacing(3)
327    .fontSize(12)
328    .border({ width: 1 })
329    .padding(10)
330    .width('100%')
331    .margin(5)
332  Text('This is the text content with letterSpacing -1.')
333    .letterSpacing(-1)
334    .fontSize(12)
335    .border({ width: 1 })
336    .padding(10)
337    .width('100%')
338    .margin(5)
339  ```
340
341  ![zh-cn_image_0000001562940513](figures/zh-cn_image_0000001562940513.png)
342
343- 通过[minFontSize](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#minfontsize)与[maxFontSize](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#maxfontsize)自适应字体大小。
344
345  minFontSize用于设置文本的最小显示字号,maxFontSize用于设置文本的最大显示字号。这两个属性必须同时设置才能生效,并且需要与[maxLines](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#maxlines)属性或布局大小限制配合使用,单独设置任一属性将不会产生效果。
346
347  ```ts
348  Text('我的最大字号为30,最小字号为5,宽度为250,maxLines为1')
349    .width(250)
350    .maxLines(1)
351    .maxFontSize(30)
352    .minFontSize(5)
353    .border({ width: 1 })
354    .padding(10)
355    .margin(5)
356  Text('我的最大字号为30,最小字号为5,宽度为250,maxLines为2')
357    .width(250)
358    .maxLines(2)
359    .maxFontSize(30)
360    .minFontSize(5)
361    .border({ width: 1 })
362    .padding(10)
363    .margin(5)
364  Text('我的最大字号为30,最小字号为15,宽度为250,高度为50')
365    .width(250)
366    .height(50)
367    .maxFontSize(30)
368    .minFontSize(15)
369    .border({ width: 1 })
370    .padding(10)
371    .margin(5)
372  Text('我的最大字号为30,最小字号为15,宽度为250,高度为100')
373    .width(250)
374    .height(100)
375    .maxFontSize(30)
376    .minFontSize(15)
377    .border({ width: 1 })
378    .padding(10)
379    .margin(5)
380  ```
381
382  ![zh-cn_image_0000001511740472](figures/zh-cn_image_0000001511740472.png)
383
384- 通过[textCase](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#textcase)属性设置文本大小写。
385
386  ```ts
387  Text('This is the text content with textCase set to Normal.')
388    .textCase(TextCase.Normal)
389    .padding(10)
390    .border({ width: 1 })
391    .padding(10)
392    .margin(5)
393  // 文本全小写展示
394  Text('This is the text content with textCase set to LowerCase.')
395    .textCase(TextCase.LowerCase)
396    .border({ width: 1 })
397    .padding(10)
398    .margin(5)
399  // 文本全大写展示
400  Text('This is the text content with textCase set to UpperCase.')
401    .textCase(TextCase.UpperCase)
402    .border({ width: 1 })
403    .padding(10)
404    .margin(5)
405  ```
406
407  ![zh-cn_image_0000001562940529](figures/zh-cn_image_0000001562940529.png)
408
409- 通过[copyOption](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#copyoption9)属性设置文本是否可复制粘贴。
410
411  ```ts
412  Text("这是一段可复制文本")
413    .fontSize(30)
414    .copyOption(CopyOptions.InApp)
415  ```
416
417  ![zh-cn_image_0000001511580868](figures/zh-cn_image_0000001511580868.png)
418
419- 通过[fontFamily](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#fontfamily)属性设置字体列表。应用当前支持'HarmonyOS Sans'字体和[注册自定义字体](../reference/apis-arkui/js-apis-font.md)。
420
421  ```ts
422  Text("This is the text content with fontFamily")
423    .fontSize(30)
424    .fontFamily('HarmonyOS Sans')
425  ```
426
427  ![Text_font_family](figures/Text_font_family.png)
428
429- 从API version 20开始,支持通过[contentTransition](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#contenttransition20)属性设置数字翻牌效果。
430
431  ```ts
432  @State number: number = 98;
433  @State numberTransition: NumericTextTransition = new NumericTextTransition({ flipDirection: FlipDirection.DOWN, enableBlur: false });
434
435  Column() {
436    Text(this.number + "")
437      .borderWidth(1)
438      .fontSize(40)
439      .contentTransition(this.numberTransition)
440    Button("chang number")
441      .onClick(() => {
442        this.number++
443      })
444      .margin(10)
445  }
446  ```
447  ![Text_content_transition](figures/Text_content_transition.gif)
448
449- 从API version 20开始,支持通过[optimizeTrailingSpace](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#optimizetrailingspace20)设置是否在文本布局过程中优化每行末尾的空格,可解决行尾空格影响对齐显示效果问题。
450
451  ```ts
452  Column() {
453    //启用优化行尾空格功能
454    Text("Trimmed space enabled     ")
455      .fontSize(30)
456      .fontWeight(FontWeight.Bold)
457      .margin({ top: 20 })
458      .optimizeTrailingSpace(true)
459      .textAlign(TextAlign.Center)
460    //不启用优化行尾空格功能
461    Text("Trimmed space disabled     ")
462      .fontSize(30)
463      .fontWeight(FontWeight.Bold)
464      .margin({ top: 20 })
465      .optimizeTrailingSpace(false)
466      .textAlign(TextAlign.Center)
467  }
468  ```
469  ![Text_optimize_trailing_space](figures/Text_optimize_trailing_space.jpg)
470
471- 从API version 20开始,支持通过[lineSpacing](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#linespacing20)设置文本的行间距。当不配置[LineSpacingOptions](../reference/apis-arkui/arkui-ts/ts-text-common.md#linespacingoptions20对象说明)时,首行上方和尾行下方默认会有行间距,当onlyBetweenLines设置为true时,行间距仅适用于行与行之间,首行上方无额外的行间距。
472
473  ```ts
474  import { LengthMetrics } from '@kit.ArkUI';
475
476  @Extend(Text)
477  function style() {
478    .width(250)
479    .height(100)
480    .maxFontSize(30)
481    .minFontSize(15)
482    .border({ width: 1 })
483  }
484
485  @Entry
486  @Component
487  struct demo {
488    build() {
489      Column() {
490        Text('The line spacing of this context is set to 20_px, and the spacing is effective only between the lines.')
491          .lineSpacing(LengthMetrics.px(20), { onlyBetweenLines: true })
492          .style()
493      }
494    }
495  }
496  ```
497  ![Text_line_spacing](figures/Text_line_spacing.jpg)
498
499- 从API version 20开始,支持通过[enableAutoSpacing](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#enableautospacing20)设置是否开启中文与西文的自动间距。
500
501  ```ts
502  @Entry
503  @Component
504  struct TextExample {
505    @State enableSpacing: boolean = false;
506
507    build() {
508      Column() {
509        Row({ space: 20 }) {
510          Button("开启自动间距")
511            .onClick(() => this.enableSpacing = true)
512            .backgroundColor(this.enableSpacing ? '#4CAF50' : '#E0E0E0')
513            .fontColor(this.enableSpacing ? Color.White : Color.Black)
514
515          Button("关闭自动间距")
516            .onClick(() => this.enableSpacing = false)
517            .backgroundColor(!this.enableSpacing ? '#F44336' : '#E0E0E0')
518            .fontColor(!this.enableSpacing ? Color.White : Color.Black)
519        }
520        .width('100%')
521        .justifyContent(FlexAlign.Center)
522        .margin({ top: 30, bottom: 20 })
523
524        Text(this.enableSpacing ? "当前状态:已开启自动间距" : "当前状态:已关闭自动间距")
525          .fontSize(16)
526          .fontColor(this.enableSpacing ? '#4CAF50' : '#F44336')
527          .margin({ bottom: 20 })
528
529        // 设置是否应用中西文自动间距
530        Text('中西文Auto Spacing自动间距')
531          .fontSize(24)
532          .padding(15)
533          .backgroundColor('#F5F5F5')
534          .width('90%')
535          .enableAutoSpacing(this.enableSpacing)
536      }
537      .width('100%')
538      .height('100%')
539      .padding(20)
540    }
541  }
542  ```
543  ![Text_enable_auto_spacing](figures/Text_enable_auto_spacing.gif)
544
545- 从API version 20开始,支持通过[ShaderStyle](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#shaderstyle20)设置渐变色。
546
547  ```ts
548  @State message: string = 'Hello World';
549  @State linearGradientOptions: LinearGradientOptions =
550    {
551      direction: GradientDirection.LeftTop,
552      colors: [[Color.Red, 0.0], [Color.Blue, 0.3], [Color.Green, 0.5]],
553      repeating: true,
554    };
555
556  Column({ space: 5 }) {
557    Text('direction为LeftTop的线性渐变').fontSize(18).width('90%').fontColor(0xCCCCCC)
558      .margin({ top: 40, left: 40 })
559    Text(this.message)
560      .fontSize(50)
561      .width('80%')
562      .height(50)
563      .shaderStyle(this.linearGradientOptions)
564  }
565  ```
566  ![Text_shader_style](figures/Text_shader_style.png)
567
568## 添加事件
569
570Text组件可以添加通用事件,可以绑定[onClick](../reference/apis-arkui/arkui-ts/ts-universal-events-click.md#onclick)、[onTouch](../reference/apis-arkui/arkui-ts/ts-universal-events-touch.md#ontouch)等事件来响应操作。
571
572```ts
573// xxx.ets
574@Entry
575@Component
576struct Index {
577  @State textStr1: string = '';
578  @State textStr2: string = '';
579
580  build() {
581    Row() {
582      Column() {
583        Text('This is a text component.')
584          .fontSize(30)
585          .onClick(() => {
586            console.info('Text onClick is triggering');
587            this.textStr1 = 'Text onClick is triggering';
588          })
589          .onTouch(() => {
590            console.info('Text onTouch is triggering');
591            this.textStr2 = 'Text onTouch is triggering';
592          })
593        Text('onClick:' + this.textStr1)
594          .fontSize(20)
595        Text('onTouch:' + this.textStr2)
596          .fontSize(20)
597      }.width('100%')
598    }
599    .height('100%')
600  }
601}
602```
603
604![text_event](figures/text_event.gif)
605
606## 设置垂直居中
607
608从API version 20开始,Text组件支持通过[textVerticalAlign](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#textverticalalign20)属性实现文本段落在垂直方向的对齐。
609
610  - 以下示例展示了如何通过textVerticalAlign属性设置文本垂直居中对齐效果。
611
612    ```ts
613    Text() {
614      Span("Hello")
615        .fontSize(50)
616      // $r('app.media.startIcon')需要替换为开发者所需的图像资源文件。
617      ImageSpan($r('app.media.startIcon'))
618        .width(30).height(30)
619        .verticalAlign(ImageSpanAlignment.FOLLOW_PARAGRAPH)
620      Span("World")
621    }
622    .textVerticalAlign(TextVerticalAlign.CENTER)
623    ```
624
625    ![Text_vertical_align](figures/Text_vertical_align.png)
626
627## 设置选中菜单
628
629### 弹出选中菜单
630
631  - 设置Text被选中时,会弹出包含复制、翻译、搜索的菜单。
632
633    Text组件需要设置[copyOption](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#copyoption9)属性才可以被选中。
634
635    ```ts
636    Text("这是一段文本,用来展示选中菜单")
637      .fontSize(30)
638      .copyOption(CopyOptions.InApp)
639    ```
640    ![Text_select_menu](figures/Text_select_menu.jpg)
641
642  - Text组件通过设置[bindSelectionMenu](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#bindselectionmenu11)属性绑定自定义选择菜单。
643
644    ```ts
645    controller:TextController = new TextController()
646    build() {
647      Column() {
648        Text("这是一段文本,用来展示选中菜单", {controller:this.controller})
649          .fontSize(30)
650          .copyOption(CopyOptions.InApp)
651          .bindSelectionMenu(TextSpanType.TEXT, this.RightClickTextCustomMenu, TextResponseType.RIGHT_CLICK, {
652            onAppear: () => {
653              console.info('自定义选择菜单弹出时触发该回调');
654            },
655            onDisappear: () => {
656              console.info('自定义选择菜单关闭时触发该回调');
657            }
658          })
659      }
660    }
661    ```
662
663    ```ts
664    // 定义菜单项
665    @Builder
666    RightClickTextCustomMenu() {
667      Column() {
668        Menu() {
669          MenuItemGroup() {
670            // $r('app.media.app_icon')需要替换为开发者所需的图像资源文件。
671            MenuItem({ startIcon: $r('app.media.app_icon'), content: "CustomMenu One", labelInfo: "" })
672              .onClick(() => {
673                // 使用closeSelectionMenu接口关闭菜单
674                this.controller.closeSelectionMenu();
675              })
676            MenuItem({ startIcon: $r('app.media.app_icon'), content: "CustomMenu Two", labelInfo: "" })
677            MenuItem({ startIcon: $r('app.media.app_icon'), content: "CustomMenu Three", labelInfo: "" })
678          }
679        }.backgroundColor('#F0F0F0')
680      }
681    }
682    ```
683    ![text_bindselectionmenu](figures/text_bindselectionmenu.gif)
684
685  - Text组件通过设置[editMenuOptions](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#editmenuoptions12)属性扩展自定义选择菜单,可以设置扩展项的文本内容、图标以及回调方法。
686
687    ```ts
688    Text('这是一段文本,用来展示选中菜单')
689      .fontSize(20)
690      .copyOption(CopyOptions.LocalDevice)
691      .editMenuOptions({
692        onCreateMenu: this.onCreateMenu, onMenuItemClick: this.onMenuItemClick
693      })
694    ```
695
696    ```ts
697    // 定义onCreateMenu,onMenuItemClick
698    onCreateMenu = (menuItems: Array<TextMenuItem>) => {
699      // $r('app.media.app_icon')需要替换为开发者所需的图像资源文件。
700      let item1: TextMenuItem = {
701        content: 'customMenu1',
702        icon: $r('app.media.app_icon'),
703        id: TextMenuItemId.of('customMenu1'),
704      };
705      let item2: TextMenuItem = {
706        content: 'customMenu2',
707        id: TextMenuItemId.of('customMenu2'),
708        icon: $r('app.media.app_icon'),
709      };
710      menuItems.push(item1);
711      menuItems.unshift(item2);
712      return menuItems;
713    }
714
715    onMenuItemClick = (menuItem: TextMenuItem, textRange: TextRange) => {
716      if (menuItem.id.equals(TextMenuItemId.of("customMenu2"))) {
717        console.log("拦截 id: customMenu2 start:" + textRange.start + "; end:" + textRange.end);
718        return true;
719      }
720      if (menuItem.id.equals(TextMenuItemId.COPY)) {
721        console.log("拦截 COPY start:" + textRange.start + "; end:" + textRange.end);
722        return true;
723      }
724      if (menuItem.id.equals(TextMenuItemId.SELECT_ALL)) {
725        console.log("不拦截 SELECT_ALL start:" + textRange.start + "; end:" + textRange.end);
726        return false;
727      }
728      return false;
729    };
730    ```
731    ![text_editmenuoptions](figures/text_editmenuoptions.gif)
732
733### 关闭选中菜单
734
735  使用Text组件时,若需要实现点击空白处关闭选中的场景,分为以下两种情况:
736
737  - 在Text组件区域内点击空白处,会正常关闭选中态和菜单;
738  - 在Text组件区域外点击空白处,前提是Text组件设置[selection](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#selection11)属性,具体示例如下:
739
740    ```ts
741    // xxx.ets
742    @Entry
743    @Component
744    struct Index {
745      @State text: string =
746        'This is set selection to Selection text content This is set selection to Selection text content.';
747      @State start: number = 0;
748      @State end: number = 20;
749
750      build() {
751        Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Start, justifyContent: FlexAlign.Start }) {
752          Text(this.text)
753            .fontSize(12)
754            .border({ width: 1 })
755            .lineHeight(20)
756            .margin(30)
757            .copyOption(CopyOptions.InApp)
758            .selection(this.start, this.end)
759            .onTextSelectionChange((selectionStart, selectionEnd) => {
760              // 更新选中态位置
761              this.start = selectionStart;
762              this.end = selectionEnd;
763            })
764        }
765        .height(600)
766        .width(335)
767        .borderWidth(1)
768        .onClick(() => {
769          // 监听父组件的点击事件,将选中首尾位置均设置为-1,即可清除选中
770          this.start = -1;
771          this.end = -1;
772        })
773      }
774    }
775    ```
776
777### 屏蔽系统服务类菜单
778
779- 从API version 20开始,支持通过[disableSystemServiceMenuItems](../reference/apis-arkui/arkts-apis-uicontext-textmenucontroller.md#disablesystemservicemenuitems20)屏蔽文本选择菜单内所有系统服务菜单项。
780
781  ```ts
782  import { TextMenuController } from '@kit.ArkUI';
783  // xxx.ets
784  @Entry
785  @Component
786  struct Index {
787    aboutToAppear(): void {
788      // 禁用所有系统服务菜单
789      TextMenuController.disableSystemServiceMenuItems(true);
790    }
791
792    aboutToDisappear(): void {
793      // 页面消失恢复系统服务菜单
794      TextMenuController.disableSystemServiceMenuItems(false);
795    }
796
797    build() {
798      Row() {
799        Column() {
800          Text("这是一段文本,长按弹出文本选择菜单")
801            .height(60)
802            .fontStyle(FontStyle.Italic)
803            .fontWeight(FontWeight.Bold)
804            .textAlign(TextAlign.Center)
805            .copyOption(CopyOptions.InApp)
806            .editMenuOptions({
807              onCreateMenu: (menuItems: Array<TextMenuItem>) => {
808                  // menuItems不包含被屏蔽的系统菜单项
809                  return menuItems;
810              },
811              onMenuItemClick: (menuItem: TextMenuItem, textRange: TextRange) => {
812                  return false;
813              }
814            })
815        }.width('100%')
816      }
817      .height('100%')
818    }
819  }
820  ```
821
822  ![text_disable_system_service_menuItems](figures/text_disable_system_service_menuItems.jpg)
823
824- 从API version 20开始,支持通过[disableMenuItems](../reference/apis-arkui/arkts-apis-uicontext-textmenucontroller.md#disablemenuitems20)屏蔽文本选择菜单内指定的系统服务菜单项。
825
826  ```ts
827  import { TextMenuController } from '@kit.ArkUI';
828  // xxx.ets
829  @Entry
830  @Component
831  struct Index {
832    aboutToAppear(): void {
833      // 禁用搜索菜单
834      TextMenuController.disableMenuItems([TextMenuItemId.SEARCH])
835    }
836
837    aboutToDisappear(): void {
838      // 恢复系统服务菜单
839      TextMenuController.disableMenuItems([])
840    }
841
842    build() {
843      Row() {
844        Column() {
845          Text("这是一段文本,长按弹出文本选择菜单")
846            .height(60)
847            .fontStyle(FontStyle.Italic)
848            .fontWeight(FontWeight.Bold)
849            .textAlign(TextAlign.Center)
850            .copyOption(CopyOptions.InApp)
851            .editMenuOptions({
852              onCreateMenu: (menuItems: Array<TextMenuItem>) => {
853                  // menuItems不包含搜索
854                  return menuItems;
855              },
856              onMenuItemClick: (menuItem: TextMenuItem, textRange: TextRange) => {
857                  return false
858              }
859            })
860        }.width('100%')
861      }
862      .height('100%')
863    }
864  }
865  ```
866
867  ![text_disable_menuItems](figures/text_disable_menuItems.jpg)
868
869### 默认菜单支持自定义刷新能力
870
871从API version 20开始,当文本选择区域变化后显示菜单之前触发[onPrepareMenu](../reference/apis-arkui/arkui-ts/ts-text-common.md#onpreparemenu20)回调,可在该回调中进行菜单数据设置。
872
873```ts
874// xxx.ets
875@Entry
876@Component
877struct TextExample12 {
878  @State text: string = 'Text editMenuOptions';
879  @State endIndex: number = 0;
880  onCreateMenu = (menuItems: Array<TextMenuItem>) => {
881    // $r('app.media.startIcon')需要替换为开发者所需的图像资源文件。
882    let item1: TextMenuItem = {
883      content: 'create1',
884      icon: $r('app.media.startIcon'),
885      id: TextMenuItemId.of('create1'),
886    };
887    let item2: TextMenuItem = {
888      content: 'create2',
889      id: TextMenuItemId.of('create2'),
890      icon: $r('app.media.startIcon'),
891    };
892    menuItems.push(item1);
893    menuItems.unshift(item2);
894    return menuItems;
895  }
896  onMenuItemClick = (menuItem: TextMenuItem, textRange: TextRange) => {
897    if (menuItem.id.equals(TextMenuItemId.of("create2"))) {
898      console.log("拦截 id: create2 start:" + textRange.start + "; end:" + textRange.end);
899      return true;
900    }
901    if (menuItem.id.equals(TextMenuItemId.of("prepare1"))) {
902      console.log("拦截 id: prepare1 start:" + textRange.start + "; end:" + textRange.end);
903      return true;
904    }
905    if (menuItem.id.equals(TextMenuItemId.COPY)) {
906      console.log("拦截 COPY start:" + textRange.start + "; end:" + textRange.end);
907      return true;
908    }
909    if (menuItem.id.equals(TextMenuItemId.SELECT_ALL)) {
910      console.log("不拦截 SELECT_ALL start:" + textRange.start + "; end:" + textRange.end);
911      return false;
912    }
913    return false;
914  }
915  // $r('app.media.startIcon')需要替换为开发者所需的图像资源文件。
916  onPrepareMenu = (menuItems: Array<TextMenuItem>) => {
917    let item1: TextMenuItem = {
918      content: 'prepare1_' + this.endIndex,
919      icon: $r('app.media.startIcon'),
920      id: TextMenuItemId.of('prepare1'),
921    };
922    menuItems.unshift(item1);
923    return menuItems;
924  }
925  @State editMenuOptions: EditMenuOptions = {
926    onCreateMenu: this.onCreateMenu,
927    onMenuItemClick: this.onMenuItemClick,
928    onPrepareMenu: this.onPrepareMenu
929  };
930
931  build() {
932    Column() {
933      Text(this.text)
934        .fontSize(20)
935        .copyOption(CopyOptions.LocalDevice)
936        .editMenuOptions(this.editMenuOptions)
937        .margin({ top: 100 })
938        .onTextSelectionChange((selectionStart: number, selectionEnd: number) => {
939          this.endIndex = selectionEnd;
940        })
941    }
942    .width("90%")
943    .margin("5%")
944  }
945}
946```
947
948![text_on_prepare_menu](figures/text_on_prepare_menu.gif)
949
950## 设置AI菜单
951
952Text组件通过[enableDataDetector](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#enabledatadetector11)和[dataDetectorConfig](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#datadetectorconfig11)属性实现AI菜单的显示。AI菜单的表现形式包括:单击AI实体(指可被识别的内容,包括地址、邮箱等)弹出菜单的实体识别选项,选中文本后,文本选择菜单与鼠标右键菜单中显示的实体识别选项。
953
954>  **说明:**
955>
956>  从API version 20开始,支持在文本选择菜单与鼠标右键菜单中显示实体识别选项。当[enableDataDetector](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#enabledatadetector11)设置为true,且[copyOption](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#copyoption9)设置为CopyOptions.LocalDevice时,该功能生效。菜单选项包括[TextMenuItemId](../reference/apis-arkui/arkui-ts/ts-text-common.md#textmenuitemid12)中的url(打开链接)、email(新建邮件)、phoneNumber(呼叫)、address(导航至该位置)、dateTime(新建日程提醒)。
957>
958>  该功能生效时,需选中范围内,包括一个完整的AI实体,才能展示对应的选项。
959
960- 如果需要单击AI实体弹出菜单的实体识别选项,可以配置[enableDataDetector](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#enabledatadetector11)为true。
961- 如果在单击的交互方式之外,还需要文本选择菜单与鼠标右键菜单中显示的实体识别选项,可以配置[enableDataDetector](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#enabledatadetector11)为true,且[copyOption](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#copyoption9)设置为CopyOptions.LocalDevice,具体示例如下所示:
962  ```ts
963  Text(
964    '电话号码:' + '(86) (755) ********' + '\n' +
965      '链接:' + 'www.********.com' + '\n' +
966      '邮箱:' + '***@example.com' + '\n' +
967      '地址:' + 'XX省XX市XX区XXXX' + '\n' +
968      '时间:' + 'XX年XX月XX日XXXX'
969  )
970    .fontSize(16)
971    .copyOption(CopyOptions.LocalDevice)
972    .enableDataDetector(true)// 使能实体识别
973    .dataDetectorConfig({
974      // 配置识别样式
975      // types可支持PHONE_NUMBER电话号码、URL链接、EMAIL邮箱、ADDRESS地址、DATE_TIME时间
976      // types设置为null或者[]时,识别所有类型的实体
977      types: [], onDetectResultUpdate: (result: string) => {
978      }
979    })
980  ```
981- 如果需要调整识别出的样式,可以通过[dataDetectorConfig](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#datadetectorconfig11)实现,具体可以参考[TextDataDetectorConfig](../reference/apis-arkui/arkui-ts/ts-text-common.md#textdatadetectorconfig11对象说明)配置项。
982- 如果需要调整菜单的位置,可以通过[editMenuOptions](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#editmenuoptions12)实现,具体可以参考示例[文本扩展自定义菜单](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#示例12文本扩展自定义菜单)。
983<!--RP2--><!--RP2End-->
984
985## 场景示例
986
987该示例通过maxLines、textOverflow、textAlign、constraintSize属性展示了热搜榜的效果。
988
989```ts
990// xxx.ets
991@Entry
992@Component
993struct TextExample {
994  build() {
995    Column() {
996      Row() {
997        Text("1").fontSize(14).fontColor(Color.Red).margin({ left: 10, right: 10 })
998        Text("我是热搜词条1")
999          .fontSize(12)
1000          .fontColor(Color.Blue)
1001          .maxLines(1)
1002          .textOverflow({ overflow: TextOverflow.Ellipsis })
1003          .fontWeight(300)
1004        Text("爆")
1005          .margin({ left: 6 })
1006          .textAlign(TextAlign.Center)
1007          .fontSize(10)
1008          .fontColor(Color.White)
1009          .fontWeight(600)
1010          .backgroundColor(0x770100)
1011          .borderRadius(5)
1012          .width(15)
1013          .height(14)
1014      }.width('100%').margin(5)
1015
1016      Row() {
1017        Text("2").fontSize(14).fontColor(Color.Red).margin({ left: 10, right: 10 })
1018        Text("我是热搜词条2 我是热搜词条2 我是热搜词条2 我是热搜词条2 我是热搜词条2")
1019          .fontSize(12)
1020          .fontColor(Color.Blue)
1021          .fontWeight(300)
1022          .constraintSize({ maxWidth: 200 })
1023          .maxLines(1)
1024          .textOverflow({ overflow: TextOverflow.Ellipsis })
1025        Text("热")
1026          .margin({ left: 6 })
1027          .textAlign(TextAlign.Center)
1028          .fontSize(10)
1029          .fontColor(Color.White)
1030          .fontWeight(600)
1031          .backgroundColor(0xCC5500)
1032          .borderRadius(5)
1033          .width(15)
1034          .height(14)
1035      }.width('100%').margin(5)
1036
1037      Row() {
1038        Text("3").fontSize(14).fontColor(Color.Orange).margin({ left: 10, right: 10 })
1039        Text("我是热搜词条3")
1040          .fontSize(12)
1041          .fontColor(Color.Blue)
1042          .fontWeight(300)
1043          .maxLines(1)
1044          .constraintSize({ maxWidth: 200 })
1045          .textOverflow({ overflow: TextOverflow.Ellipsis })
1046        Text("热")
1047          .margin({ left: 6 })
1048          .textAlign(TextAlign.Center)
1049          .fontSize(10)
1050          .fontColor(Color.White)
1051          .fontWeight(600)
1052          .backgroundColor(0xCC5500)
1053          .borderRadius(5)
1054          .width(15)
1055          .height(14)
1056      }.width('100%').margin(5)
1057
1058      Row() {
1059        Text("4").fontSize(14).fontColor(Color.Grey).margin({ left: 10, right: 10 })
1060        Text("我是热搜词条4 我是热搜词条4 我是热搜词条4 我是热搜词条4 我是热搜词条4")
1061          .fontSize(12)
1062          .fontColor(Color.Blue)
1063          .fontWeight(300)
1064          .constraintSize({ maxWidth: 200 })
1065          .maxLines(1)
1066          .textOverflow({ overflow: TextOverflow.Ellipsis })
1067      }.width('100%').margin(5)
1068    }.width('100%')
1069  }
1070}
1071
1072```
1073
1074![zh-cn_image_0000001562820805](figures/zh-cn_image_0000001562820805.png)
1075<!--RP1--><!--RP1End-->
1076
1077## 常见问题
1078
1079### Text组件尾部省略号后为什么还有一段空白,没有占满组件宽度
1080
1081**问题现象**
1082
1083在Text组件上未设置宽度,当内容过长时,省略号与组件边缘之间会留有较大空白,且内容更新时省略号的位置会发生变化。
1084
1085![](figures/EllipsisDemo1.gif)
1086
1087**原因分析**
1088
1089当Text组件未设置宽度且内容超长时,组件宽度将采用父组件传递的布局约束的最大宽度。省略开始位置会根据不同的断词模式导致排版塑型结果有所不同,因此不同内容的省略开始位置也会不同。
1090
1091**解决措施**
1092
1093设置[wordBreak](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#wordbreak11)属性为`WordBreak.BREAK_ALL`,任意2个字符间断行使文本内容尽量占满组件区域。
1094
1095示例代码如下:
1096```ts
1097@Entry
1098@Component
1099struct Index {
1100  @State message: string = '混合Hello World! honorificabilitudinitatibus!';
1101
1102  build() {
1103    Column() {
1104      Text(this.message)
1105        .id('HelloWorld')
1106        .fontSize('25fp')
1107        .maxLines(1)
1108        .textOverflow({ overflow: TextOverflow.Ellipsis})
1109        .onClick(() => {
1110          this.message = 'Welcome try try try 1235628327434348';
1111        })
1112        .border({ width: 1})
1113        .wordBreak(WordBreak.BREAK_ALL) // 修改断词模式
1114    }
1115    .width(300)
1116    .border({ width: 1, color: Color.Blue})
1117    .margin({left: 30, top: 50})
1118  }
1119}
1120```
1121
1122### Text组件如何实现行末展开样式
1123
1124**解决措施**
1125
1126自行测算截断字符,并在行末添加`...展开`或者`...图标`作为组件内容。
1127
1128**参考链接**
1129
1130[属性字符串转Paragraph数组](../reference/apis-arkui/arkts-apis-uicontext-measureutils.md#getparagraphs20)
1131<!--RP3--><!--RP3End-->
1132
1133### Text组件如何实现不设置maxLines在固定布局约束下内容超出仍显示省略样式
1134
1135**问题现象**
1136
1137在固定尺寸的组件区域内,不同字号的内容显示的最大行数会有所不同。期望实现内容超长时自动显示省略样式,则无需设置固定的`maxLines`值。
1138
1139**解决措施**
1140
1141设置[heightAdaptivePolicy](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#heightadaptivepolicy10)为TextHeightAdaptivePolicy.LAYOUT_CONSTRAINT_FIRST,该模式会删除超过布局约束的行,从而实现类似设置maxLines的效果。
1142
1143示例代码如下:
1144```ts
1145@Entry
1146@Component
1147struct Index {
1148  @State message: string = '混合Hello World! 多行文本 中英文数字混合 1282378283 ~';
1149  @State fontSize: number = 25;
1150
1151  build() {
1152    Column({ space: 10 }) {
1153      Text(this.message)
1154        .id('HelloWorld')
1155        .fontSize(this.fontSize)
1156        .textOverflow({ overflow: TextOverflow.Ellipsis})
1157        .border({ width: 1})
1158        .heightAdaptivePolicy(TextHeightAdaptivePolicy.LAYOUT_CONSTRAINT_FIRST) // 调整自适应布局策略
1159        .width(300)
1160        .height(200)
1161      Row(){
1162        Button('fontSize+5')
1163          .onClick(()=>{
1164            this.fontSize += 5;
1165          })
1166        Button('fontSize-5')
1167          .onClick(()=>{
1168            this.fontSize -= 5;
1169          })
1170      }
1171    }
1172    .margin({left: 30, top: 50})
1173  }
1174}
1175```
1176![](figures/EllipsisDemo2.gif)
1177