• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 富文本编辑(RichEditor)
2<!--Kit: ArkUI-->
3<!--Subsystem: ArkUI-->
4<!--Owner: @carnivore233-->
5<!--Designer: @pssea-->
6<!--Tester: @mateng_Holtens-->
7<!--Adviser: @HelloCrease-->
8
9RichEditor是支持图文混排和文本交互式编辑的组件,通常用于响应用户对图文混合内容的输入操作,例如可以输入图文的评论区。具体用法参考[RichEditor](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md)。
10
11对于仅需图文展示而不需要编辑的场景,推荐使用[Text](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md)组件。
12
13对于需要大量展示Html格式内容的场景,推荐使用[RichText](../reference/apis-arkui/arkui-ts/ts-basic-components-richtext.md)组件。
14
15## 组件构成
16
17下图展示了组件元素的构成。
18
19![alt text](figures/RichEditor_guide_composition.jpg)
20
21组件的元素构成包括:
22
23| 元素  | 说明                              |
24| --- | ------------------------------- |
25| 内容区 | 内容可显示的区域。                       |
26| 光标  | 用于指明当前输入位置。                     |
27| 手柄  | 分为左手柄和右手柄,可分别进行拖动,用于调整文本选择区域范围。 |
28| 菜单  | 选中内容后弹出,其中包含复制、粘贴等内容操作按钮。       |
29
30## 创建RichEditor组件
31
32开发者可以[创建基于属性字符串进行内容管理的RichEditor组件](#创建基于属性字符串进行内容管理的richeditor组件)或[创建基于Span进行内容管理的RichEditor组件](#创建基于span进行内容管理的richeditor组件)。
33
34### 创建基于属性字符串进行内容管理的RichEditor组件
35
36使用RichEditor(options: [RichEditorStyledStringOptions](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#richeditorstyledstringoptions12))接口可以创建基于属性字符串([StyledString/MutableStyledString](arkts-styled-string.md))进行内容管理的RichEditor组件。这种构建方式开发者可以通过在应用侧持有属性字符串对象来管理数据,通过修改属性字符串对象的内容、样式,再传递给组件,即可实现对富文本组件内容的更新。
37
38相比于使用controller提供的接口进行内容样式更新,使用起来更加灵活便捷。同时属性字符串对象可以设置到各类支持属性字符串的文本组件中,可以快速实现内容的迁移。
39
40```ts
41fontStyle: TextStyle = new TextStyle({
42  fontColor: Color.Pink
43});
44// 定义字体样式对象
45
46mutableStyledString: MutableStyledString = new MutableStyledString("创建使用属性字符串构建的RichEditor组件。",
47  [{
48    start: 0,
49    length: 5,
50    styledKey: StyledStringKey.FONT,
51    styledValue: this.fontStyle
52  }]);
53// 创建属性字符串
54
55controller: RichEditorStyledStringController = new RichEditorStyledStringController();
56options: RichEditorStyledStringOptions = { controller: this.controller };
57
58RichEditor(this.options)
59  .onReady(() => {
60    this.controller.setStyledString(this.mutableStyledString);
61  })
62```
63
64![alt text](figures/richeditor_image_stylestringoptions.gif)
65
66### 创建基于Span进行内容管理的RichEditor组件
67
68使用RichEditor(value: [RichEditorOptions](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#richeditoroptions))接口可以创建基于Span进行内容管理的RichEditor组件,通常用于复杂内容场景,开发者通过RichEditorController提供的接口实现内容、样式的管理。
69
70```ts
71@Entry
72@Component
73struct create_rich_editor {
74  controller: RichEditorController = new RichEditorController();
75  options: RichEditorOptions = { controller: this.controller };
76
77  build() {
78    Column() {
79      Column() {
80        RichEditor(this.options)
81          .onReady(() => {
82            this.controller.addTextSpan('创建不使用属性字符串构建的RichEditor组件。', {
83              style: {
84                fontColor: Color.Black,
85                fontSize: 15
86              }
87            })
88          })
89      }.width('100%')
90    }.height('100%')
91  }
92}
93```
94
95![alt text](figures/richeditor_image_options.gif)
96
97## 添加内容
98
99富文本组件可以通过不同的接口添加多种形式的内容。
100
101### 添加文本内容
102
103除了直接在组件内输入内容,也可以通过[addTextSpan](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#addtextspan)添加文本内容。
104
105此接口可以实现文本样式多样化,例如创建混合样式文本。
106
107如果组件是获焦状态并且光标在闪烁,那么通过addTextSpan添加文本内容后,光标位置会更新,在新添加文本内容的右侧闪烁。
108
109```ts
110@Entry
111@Component
112struct add_text_span {
113  controller: RichEditorController = new RichEditorController();
114  options: RichEditorOptions = { controller: this.controller };
115
116  build() {
117    Column() {
118      RichEditor(this.options)
119        .onReady(() => {
120          this.controller.addTextSpan('点击按钮在此处添加text。', {
121            style: {
122              fontColor: Color.Black,
123              fontSize: 15
124            }
125          })
126        })
127        .border({ width: 1, color: Color.Gray })
128        .constraintSize({
129          maxHeight: 100
130        })
131        .width(300)
132        .margin(10)
133      Button('addTextSpan', {
134        buttonStyle: ButtonStyleMode.NORMAL
135      })
136        .height(30)
137        .fontSize(13)
138        .onClick(() => {
139          this.controller.addTextSpan('新添加一段文字。')
140        })
141    }
142  }
143}
144```
145
146![alt text](figures/richeditor_image_add_text.gif)
147
148### 添加图片内容
149
150通过[addImageSpan](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#addimagespan)添加图片内容。
151
152此接口可用于内容丰富与可视化展示,例如在新闻中加入图片,在文档中加入数据可视化图形等。
153
154如果组件是获焦状态并且光标在闪烁,那么通过addImageSpan添加图片内容后,光标位置会更新,在新添加图片内容的右侧闪烁。
155
156```ts
157controller: RichEditorController = new RichEditorController();
158options: RichEditorOptions = { controller: this.controller };
159
160RichEditor(this.options)
161  .onReady(() => {
162    this.controller.addTextSpan('点击按钮在此处添加image。', {
163      style: {
164        fontColor: Color.Black,
165        fontSize: 15
166      }
167    })
168  })
169  .width(300)
170  .height(100)
171Button('addImageSpan', {
172  buttonStyle: ButtonStyleMode.NORMAL
173})
174  .height(30)
175  .fontSize(13)
176  .onClick(() => {
177    this.controller.addImageSpan($r("app.media.startIcon"), {
178      imageStyle: {
179        size: ["57px", "57px"]
180      }
181    })
182  })
183```
184
185![alt text](figures/richeditor_image_add_image.gif)
186
187### 添加@Builder装饰器修饰的内容
188
189通过[addBuilderSpan](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#addbuilderspan11)添加@Builder装饰器修饰的内容。
190
191此接口可用于自定义复杂组件的嵌入,例如在组件内加入自定义图表。
192
193该接口内可通过[RichEditorBuilderSpanOptions](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#richeditorbuilderspanoptions11)设置在组件中添加builder的位置,省略或者为异常值时,则添加builder到所有内容的最后位置。
194
195```ts
196private my_builder: CustomBuilder = undefined
197
198@Builder
199TextBuilder() {
200  Row() {
201    Image($r('app.media.startIcon')).width(50).height(50).margin(16)
202    Column() {
203      Text("文本文档.txt").fontWeight(FontWeight.Bold).fontSize(16)
204      Text("123.45KB").fontColor('#8a8a8a').fontSize(12)
205    }.alignItems(HorizontalAlign.Start)
206  }.backgroundColor('#f4f4f4')
207  .borderRadius("20")
208  .width(220)
209}
210
211controller: RichEditorController = new RichEditorController();
212options: RichEditorOptions = { controller: this.controller };
213
214Button('addBuilderSpan', {
215  buttonStyle: ButtonStyleMode.NORMAL
216})
217  .height(30)
218  .fontSize(13)
219  .onClick(() => {
220    this.my_builder = () => {
221      this.TextBuilder()
222    }
223    this.controller.addBuilderSpan(this.my_builder)
224  })
225```
226
227![alt text](figures/richeditor_image_add_builder_span2.0.gif)
228
229### 添加SymbolSpan内容
230
231可通过[addSymbolSpan](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#addsymbolspan11)添加Symbol内容。此接口可用于特殊符号的添加,例如在编辑学术论文时,此接口可用于添加各种数学符号。
232
233添加Symbol内容时,如果组件是获焦状态并且光标在闪烁,那么添加Symbol后,光标将移动到新插入Symbol的右侧。
234
235Symbol内容暂不支持手势、复制、拖拽处理。
236
237```ts
238controller: RichEditorController = new RichEditorController();
239options: RichEditorOptions = { controller: this.controller };
240
241RichEditor(this.options)
242  .onReady(() => {
243    this.controller.addTextSpan('点击按钮在此处添加symbol。', {
244      style: {
245        fontColor: Color.Black,
246        fontSize: 15
247      }
248    })
249  })
250  .width(300)
251  .height(100)
252Button('addSymbolSpan', {
253  buttonStyle: ButtonStyleMode.NORMAL
254})
255  .height(30)
256  .fontSize(13)
257  .onClick(() => {
258    this.controller.addSymbolSpan($r("sys.symbol.basketball_fill"), {
259      style: {
260        fontSize: 30
261      }
262    })
263  })
264```
265
266![alt text](figures/richeditor_image_add_SymbolSpan.gif)
267
268## 管理内容
269
270富文本组件可以通过接口对内容进行管理,例如[获取组件内的图文信息](#获取组件内图文信息)、[设置无输入时的提示文本](#设置无输入时的提示文本)或[设置组件内容的最大字符数](#设置最大长度)。
271
272### 获取组件内图文信息
273
274可通过[getSpans](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#getspans)获取组件内所有图文内容的信息,包括图文的内容、id、样式、位置等信息。获取内容位置信息后,可对指定范围内容进行样式的更新。
275
276此接口适用于已有的内容样式获取与检查,例如在模板应用场景下,可利用此接口获取文本样式。此外,它还适用于内容解析与处理,例如在文本分析应用中,此接口能够获取特定范围内的文本信息。
277
278```ts
279controller: RichEditorController = new RichEditorController();
280options: RichEditorOptions = { controller: this.controller }
281infoShowController: RichEditorController = new RichEditorController();
282infoShowOptions: RichEditorOptions = { controller: this.infoShowController }
283// 创建两个富文本组件
284
285RichEditor(this.options)
286  .onReady(() => {
287    this.controller.addTextSpan('点击按钮获取此处span信息。', {
288      style: {
289        fontColor: Color.Black,
290        fontSize: 15
291      }
292    })
293  })
294  .width(300)
295  .height(50)
296Text('查看getSpans返回值:').fontSize(10).fontColor(Color.Gray).width(300)
297RichEditor(this.infoShowOptions)
298  .width(300)
299  .height(50)
300Button('getSpans', {
301  buttonStyle: ButtonStyleMode.NORMAL
302})
303  .height(30)
304  .fontSize(13)
305  .onClick(() => {
306    this.infoShowController.addTextSpan(JSON.stringify(this.controller.getSpans()), {
307      style: {
308        fontColor: Color.Gray,
309        fontSize: 10
310      }
311    })
312  })
313```
314
315![alt text](figures/richeditor_image_getspan.gif)
316
317<!--RP1--><!--RP1End-->
318
319### 设置无输入时的提示文本
320
321通过[placeholder](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#placeholder12)设置无输入时的提示文本。
322
323例如,在用户登录界面采用提示文本,有助于用户区分用户名与密码的输入框。又如,在文本编辑框中,使用提示文本明确输入要求,如“限输入100字以内”,以此指导用户正确操作。
324
325```ts
326controller: RichEditorController = new RichEditorController();
327options: RichEditorOptions = { controller: this.controller };
328
329RichEditor(this.options)
330  .placeholder("此处为提示文本...", {
331    fontColor: Color.Gray,
332    font: {
333      size: 15,
334      weight: FontWeight.Normal,
335      family: "HarmonyOS Sans",
336      style: FontStyle.Normal
337    }
338  })
339  .width(300)
340  .height(50)
341```
342
343![alt text](figures/richeditor_image_placeholder.gif)
344
345### 设置最大长度
346
347通过[maxLength](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#maxlength18)可以设置富文本的最大可输入字符数。
348
349```ts
350controller: RichEditorController = new RichEditorController();
351options: RichEditorOptions = { controller: this.controller };
352
353RichEditor(this.options)
354  .placeholder('组件设置了最大字符数:7')
355  .onReady(() => {})
356  .maxLength(7)
357```
358
359![max Length](figures/RichEditor_maxLength.gif)
360
361## 事件回调
362
363开发者可以通过注册事件回调,感知组件事件的触发。
364
365### 添加图文变化前和图文变化后可触发的回调
366
367通过[onWillChange](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#onwillchange12)添加图文变化前可触发的回调。此回调适用于用户实时数据校验与提醒,例如在用户输入文本时,可在回调内实现对输入内容的检测,若检测到敏感词汇,应立即弹出提示框。此外,它还适用于实时字数统计与限制,对于有字数限制的输入场景,可在回调中实时统计用户输入的字数,并在接近字数上限时提供相应的提示。
368
369通过[onDidChange](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#ondidchange12)添加图文变化后可触发的回调。此回调适用于内容保存与同步,例如在用户完成内容编辑后,可使用该回调自动将最新内容保存至本地或同步至服务器。此外,它还适用于内容状态更新与渲染,例如在待办事项列表应用中,用户编辑富文本格式的待办事项描述后,可使用该回调更新待办事项在列表中的显示样式。
370
371使用[RichEditorStyledStringOptions](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#richeditorstyledstringoptions12)构建的RichEditor组件不支持上述两种回调。
372
373```ts
374controller: RichEditorController = new RichEditorController();
375options: RichEditorOptions = { controller: this.controller };
376
377infoShowController: RichEditorController = new RichEditorController();
378infoShowOptions: RichEditorOptions = { controller: this.infoShowController };
379
380RichEditor(this.options)
381  .onReady(() => {
382    this.controller.addTextSpan('组件内图文变化前,触发回调。\n图文变化后,触发回调。', {
383      style: {
384        fontColor: Color.Black,
385        fontSize: 15
386      }
387    })
388  })
389  .onWillChange((value: RichEditorChangeValue) => {
390    this.infoShowController.addTextSpan('组件内图文变化前,触发回调:\n' + JSON.stringify(value), {
391      style: {
392        fontColor: Color.Gray,
393        fontSize: 10
394      }
395    })
396    return true;
397  })
398  .onDidChange((rangeBefore: TextRange, rangeAfter: TextRange) => {
399    this.infoShowController.addTextSpan('\n图文变化后,触发回调:\nrangeBefore:' + JSON.stringify(rangeBefore) +
400      '\nrangeAfter:' + JSON.stringify(rangeAfter), {
401      style: {
402        fontColor: Color.Gray,
403        fontSize: 10
404      }
405    })
406  })
407  .width(300)
408  .height(50)
409Text('查看回调内容:').fontSize(10).fontColor(Color.Gray).width(300)
410RichEditor(this.infoShowOptions)
411  .width(300)
412  .height(70)
413```
414
415![alt text](figures/richeditor_image_ondid.gif)
416
417### 添加输入法输入内容前和完成输入后可触发的回调
418
419添加输入法输入内容前和完成输入后可触发的回调。
420
421在添加输入法输入内容前,可以通过[aboutToIMEInput](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#abouttoimeinput)触发回调。在输入法完成输入后,可以通过[onDidIMEInput](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#ondidimeinput12)触发回调。
422
423这两种回调机制适用于文本上屏过程的业务逻辑处理。例如:在用户输入的文本上屏前,利用回调提供联想词汇,在用户完成输入后,执行自动化纠错或格式转换。两种回调的时序依次为:aboutToIMEInput、onDidIMEInput。
424
425使用[RichEditorStyledStringOptions](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#richeditorstyledstringoptions12)构建的组件不支持上述两种回调功能。
426
427```ts
428controller: RichEditorController = new RichEditorController();
429options: RichEditorOptions = { controller: this.controller };
430
431infoShowController: RichEditorController = new RichEditorController();
432infoShowOptions: RichEditorOptions = { controller: this.infoShowController };
433
434RichEditor(this.options)
435  .onReady(() => {
436    this.controller.addTextSpan('输入法输入内容前,触发回调。\n输入法完成输入后,触发回调。', {
437      style: {
438        fontColor: Color.Black,
439        fontSize: 15
440      }
441    })
442  })
443  .aboutToIMEInput((value: RichEditorInsertValue) => {
444    this.infoShowController.addTextSpan('输入法输入内容前,触发aboutToIMEInput回调:\n' + JSON.stringify(value), {
445      style: {
446        fontColor: Color.Gray,
447        fontSize: 10
448      }
449    })
450    return true;
451  })
452  .onDidIMEInput((value: TextRange) => {
453    this.infoShowController.addTextSpan('输入法完成输入后,触发onDidIMEInput回调:\n' + JSON.stringify(value), {
454      style: {
455        fontColor: Color.Gray,
456        fontSize: 10
457      }
458    })
459        })
460  .width(300)
461  .height(50)
462Text('查看回调内容:').fontSize(10).fontColor(Color.Gray).width(300)
463RichEditor(this.infoShowOptions)
464  .width(300)
465  .height(70)
466```
467
468![alt text](figures/richeditor_image_aboutToIMEInput4.gif)
469
470### 添加完成粘贴前可触发的回调
471
472通过[onPaste](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#onpaste11)回调,来添加粘贴前要处理的流程。
473
474此回调适用于内容格式的处理。例如,当用户复制包含HTML标签的文本时,可在回调中编写代码,将其转换为富文本组件所支持的格式,同时剔除不必要的标签或仅保留纯文本内容。
475
476由于组件默认的粘贴行为仅限于纯文本,无法处理图片粘贴,开发者可利用此方法实现图文并茂的粘贴功能,从而替代组件原有的粘贴行为。
477
478```ts
479import { BusinessError, pasteboard } from '@kit.BasicServicesKit';
480
481@Entry
482@Component
483struct on_cut_copy_paste {
484  controller: RichEditorController = new RichEditorController();
485  options: RichEditorOptions = { controller: this.controller }
486  infoShowController: RichEditorController = new RichEditorController();
487  infoShowOptions: RichEditorOptions = { controller: this.infoShowController }
488
489  popDataFromPasteboard() {
490    let selection = this.controller.getSelection();
491    let start = selection.selection[0];
492    let end = selection.selection[1];
493    if (start == end) {
494      start = this.controller.getCaretOffset();
495      end = start;
496    }
497    let moveOffset = 0;
498    let sysBoard = pasteboard.getSystemPasteboard();
499    sysBoard.getData((err, data) => {
500      if (err) {
501        return;
502      }
503      if (start != end) {
504        this.controller.deleteSpans({ start: start, end: end })
505      }
506      let count = data.getRecordCount();
507      for (let i = 0; i < count; i++) {
508        const element = data.getRecord(i);
509        if (element && element.plainText && element.mimeType === pasteboard.MIMETYPE_TEXT_PLAIN) {
510          this.controller.addTextSpan(element.plainText,
511            {
512              style: { fontSize: 26, fontColor: Color.Red },
513              offset: start + moveOffset
514            }
515          )
516          moveOffset += element.plainText.length;
517        }
518      }
519      this.controller.setCaretOffset(start + moveOffset)
520    })
521  }
522
523  build() {
524    Column() {
525      Column({ space: 3 }) {
526        RichEditor(this.options)
527          .onReady(() => {
528            this.controller.addTextSpan('对此处文本进行复制粘贴操作可触发对应回调。',
529              { style: { fontColor: Color.Black, fontSize: 15 } })
530          })
531          .onPaste((event) => {
532            this.infoShowController.addTextSpan('触发onPaste回调\n', { style: { fontColor: Color.Gray, fontSize: 10 } })
533            if (event != undefined && event.preventDefault) {
534              event.preventDefault();
535            }
536            console.info('RichEditor onPaste')
537            this.popDataFromPasteboard()
538          })
539          .width(300)
540          .height(70)
541        Text('查看回调内容:').fontSize(10).fontColor(Color.Gray).width(300)
542          .width(300)
543          .height(70)
544        RichEditor(this.infoShowOptions)
545          .width(300)
546          .height(70)
547      }.width('100%').alignItems(HorizontalAlign.Start)
548    }.height('100%')
549  }
550}
551```
552
553### 添加完成剪切前可触发的回调
554
555通过[onCut](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#oncut12)回调,来添加剪切前要处理的流程。
556
557此回调功能适用于数据处理与存储。例如,当用户从富文本组件中剪切内容时,可在回调中临时存储被剪切的内容,确保后续的粘贴操作能够准确无误地还原内容。
558
559由于组件默认的剪切行为仅限于纯文本,无法处理图片剪切,开发者可利用此方法实现图文并茂的剪切功能,从而替代组件原有的剪切行为。
560
561```ts
562controller: RichEditorController = new RichEditorController();
563options: RichEditorOptions = { controller: this.controller };
564
565infoShowController: RichEditorController = new RichEditorController();
566infoShowOptions: RichEditorOptions = { controller: this.infoShowController };
567
568RichEditor(this.options)
569  .onReady(() => {
570    this.controller.addTextSpan('对此处文本进行复制粘贴操作可触发对应回调。', {
571      style: {
572        fontColor: Color.Black,
573        fontSize: 15
574      }
575    })
576  })
577  .onCut(() => {
578    this.infoShowController.addTextSpan('触发onCut回调\n', {
579      style: {
580        fontColor: Color.Gray,
581        fontSize: 10
582      }
583    })
584  })
585  .width(300)
586  .height(70)
587RichEditor(this.infoShowOptions)
588  .width(300)
589  .height(70)
590```
591
592### 添加完成复制前可触发的回调
593
594通过[onCopy](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#oncopy12)回调,来添加复制前要处理的流程。
595
596此回调适用于内容的备份与共享,例如在用户复制内容时,可在回调中执行以下操作:将复制的内容及其格式信息保存至本地备份文件夹,或自动生成一段包含复制内容及产品购买链接的分享文案,以方便用户进行粘贴和分享。
597
598组件默认的复制行为仅限于纯文本,无法处理图片。开发者可利用此方法实现图文并茂的复制功能,替代组件的默认行为。
599
600```ts
601controller: RichEditorController = new RichEditorController();
602options: RichEditorOptions = { controller: this.controller };
603
604infoShowController: RichEditorController = new RichEditorController();
605infoShowOptions: RichEditorOptions = { controller: this.infoShowController };
606
607RichEditor(this.options)
608  .onReady(() => {
609    this.controller.addTextSpan('对此处文本进行复制粘贴操作可触发对应回调。', {
610      style: {
611        fontColor: Color.Black,
612        fontSize: 15
613      }
614    })
615  })
616  .onCopy(() => {
617    this.infoShowController.addTextSpan('触发onCopy回调\n', {
618      style: {
619        fontColor: Color.Gray,
620        fontSize: 10
621      }
622    })
623  })
624  .width(300)
625  .height(70)
626RichEditor(this.infoShowOptions)
627  .width(300)
628  .height(70)
629```
630
631![alt text](figures/richeditor_image_oncut_paste_copy.gif)
632
633更多事件使用请参考[RichEditor事件](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#事件)。
634
635## 组件交互
636
637可以通过接口配置交互元素属性,感知交互元素变化。
638
639### 设置输入框光标和手柄的颜色
640
641通过[caretColor](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#caretcolor12)设置输入框光标和手柄的颜色。
642
643设置不同颜色的光标和手柄可以提高视觉辨识度,特别是在包含多个输入区域的复杂界面中,独特的光标颜色能帮助快速定位当前操作的输入区域。这一特性也可以提升用户体验,使光标颜色与应用页面整体的风格相协调。
644
645```ts
646controller: RichEditorController = new RichEditorController();
647options: RichEditorOptions = { controller: this.controller };
648
649RichEditor(this.options)
650  .onReady(() => {
651    this.controller.addTextSpan('组件设置了光标手柄颜色。', {
652      style: {
653        fontColor: Color.Black,
654        fontSize: 15
655      }
656    })
657  })
658  .caretColor(Color.Orange)
659  .width(300)
660  .height(300)
661```
662
663![alt text](figures/richeditor_image_caretcolor.gif)
664
665### 添加组件内容选择区域或编辑状态下光标位置改变时可触发的回调
666
667通过[onSelectionChange](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#onselectionchange12)来添加组件内容选择区域或编辑状态下光标位置改变时可触发的回调。
668
669该回调可用于实时监听组件内容选中区域变化,例如实现实时更新工具栏状态(显示字体、段落格式等)、统计选中内容长度或生成选中内容摘要。实时响应选中状态,动态联动交互元素,提升富文本编辑的操作反馈体验和功能的灵活性。
670
671```ts
672controller: RichEditorController = new RichEditorController();
673options: RichEditorOptions = { controller: this.controller };
674
675infoShowController: RichEditorController = new RichEditorController();
676infoShowOptions: RichEditorOptions = { controller: this.infoShowController };
677
678RichEditor(this.options)
679  .onReady(() => {
680    this.controller.addTextSpan('改变内容选择区域或编辑状态下的光标位置,触发onSelectionChange回调。', {
681      style: {
682        fontColor: Color.Black,
683        fontSize: 15
684      }
685    })
686  })
687  .onSelectionChange((value: RichEditorRange) => {
688    this.infoShowController.addTextSpan("\n" + "触发了onSelectionChange回调,起始范围信息为:(" + value.start + "," +
689    value.end + ")", {
690      style: {
691        fontColor: Color.Gray,
692        fontSize: 10
693      }
694    })
695  })
696  .width(300)
697  .height(50)
698Text('查看回调内容:').fontSize(10).fontColor(Color.Gray).width(300)
699RichEditor(this.infoShowOptions)
700  .width(300)
701  .height(70)
702```
703
704![alt text](figures/richeditor_image_onSelectionChange.gif)
705
706### 设置内容选中区范围
707
708通过[setSelection](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#setselection11)设置组件内的内容选中时部分背板高亮。
709
710此接口可用于实现文本聚焦效果,例如当用户点击某个文本段落的标题或摘要时,可通过该接口自动选中并高亮出对应正文内容。
711
712当组件内未获焦出现光标时,调用该接口不产生选中效果。
713
714```ts
715controller: RichEditorController = new RichEditorController();
716options: RichEditorOptions = { controller: this.controller };
717
718RichEditor(this.options)
719  .onReady(() => {
720    this.controller.addTextSpan('点击按钮在此处选中0-2位置的文本。', {
721      style: {
722        fontColor: Color.Black,
723        fontSize: 15
724      }
725    })
726  })
727  .width(300)
728  .height(60)
729Button('setSelection(0,2)', {
730  buttonStyle: ButtonStyleMode.NORMAL
731})
732  .height(30)
733  .fontSize(13)
734  .onClick(() => {
735    this.controller.setSelection(0, 2)
736  })
737```
738
739![alt text](figures/richeditor_image_set_selection.gif)
740
741## 菜单配置
742
743通过接口可以对文本选择菜单进行配置。
744
745### 管理选中菜单项
746
747当富文本选择区域变化后显示菜单之前触发[onPrepareMenu](../reference/apis-arkui/arkui-ts/ts-text-common.md#onpreparemenu20)回调,可在该回调中进行菜单数据设置。
748
749```ts
750// xxx.ets
751@Entry
752@Component
753struct RichEditorExample {
754  controller: RichEditorController = new RichEditorController();
755  options: RichEditorOptions = { controller: this.controller };
756  @State endIndex: number | undefined = 0;
757  onCreateMenu = (menuItems: Array<TextMenuItem>) => {
758    const idsToFilter = [
759      TextMenuItemId.TRANSLATE,
760      TextMenuItemId.SHARE,
761      TextMenuItemId.SEARCH,
762      TextMenuItemId.AI_WRITER
763    ]
764    const items = menuItems.filter(item => !idsToFilter.some(id => id.equals(item.id)))
765    let item1: TextMenuItem = {
766      content: 'create1',
767      icon: $r('app.media.startIcon'),
768      id: TextMenuItemId.of('create1'),
769    };
770    let item2: TextMenuItem = {
771      content: 'create2',
772      id: TextMenuItemId.of('create2'),
773      icon: $r('app.media.startIcon'),
774    };
775    items.push(item1);
776    items.unshift(item2);
777    return items;
778  }
779  onMenuItemClick = (menuItem: TextMenuItem, textRange: TextRange) => {
780    if (menuItem.id.equals(TextMenuItemId.of("create2"))) {
781      console.info("拦截 id: create2 start:" + textRange.start + "; end:" + textRange.end);
782      return true;
783    }
784    if (menuItem.id.equals(TextMenuItemId.of("prepare1"))) {
785      console.info("拦截 id: prepare1 start:" + textRange.start + "; end:" + textRange.end);
786      return true;
787    }
788    if (menuItem.id.equals(TextMenuItemId.COPY)) {
789      console.info("拦截 COPY start:" + textRange.start + "; end:" + textRange.end);
790      return true;
791    }
792    if (menuItem.id.equals(TextMenuItemId.SELECT_ALL)) {
793      console.info("不拦截 SELECT_ALL start:" + textRange.start + "; end:" + textRange.end);
794      return false;
795    }
796    return false;
797  }
798  onPrepareMenu = (menuItems: Array<TextMenuItem>) => {
799    let item1: TextMenuItem = {
800      content: 'prepare1_' + this.endIndex,
801      icon: $r('app.media.startIcon'),
802      id: TextMenuItemId.of('prepare1'),
803    };
804    menuItems.unshift(item1);
805    return menuItems;
806  }
807  @State editMenuOptions: EditMenuOptions = {
808    onCreateMenu: this.onCreateMenu,
809    onMenuItemClick: this.onMenuItemClick,
810    onPrepareMenu: this.onPrepareMenu
811  };
812
813  build() {
814    Column() {
815      RichEditor(this.options)
816        .onReady(() => {
817          this.controller.addTextSpan("RichEditor editMenuOptions");
818        })
819        .editMenuOptions(this.editMenuOptions)
820        .onSelectionChange((range: RichEditorRange) => {
821          console.info("onSelectionChange, (" + range.start + "," + range.end + ")");
822          this.endIndex = range.end;
823        })
824        .height(50)
825        .margin({ top: 100 })
826        .borderWidth(1)
827        .borderColor(Color.Red)
828    }
829    .width("90%")
830    .margin("5%")
831  }
832}
833```
834
835![alt text](figures/richeditor_on_prepare_menu.gif)
836
837### 屏蔽系统服务类菜单项
838
839通过[disableSystemServiceMenuItems](../reference/apis-arkui/arkts-apis-uicontext-textmenucontroller.md#disablesystemservicemenuitems20)屏蔽富文本选择菜单内所有系统服务菜单项。
840
841此接口保护内容安全,适用于限制文本操作的场景,例如展示保密内容或禁止复制的版权文本。屏蔽系统服务菜单项,防止用户通过系统服务菜单复制、分享文本,降低内容泄露风险。
842
843
844  ```ts
845  import { TextMenuController } from '@kit.ArkUI';
846
847  // xxx.ets
848  @Entry
849  @Component
850  struct Index {
851    controller: RichEditorController = new RichEditorController();
852    options: RichEditorOptions = { controller: this.controller };
853
854    aboutToAppear(): void {
855      // 禁用所有系统服务菜单
856      TextMenuController.disableSystemServiceMenuItems(true);
857    }
858
859    aboutToDisappear(): void {
860      // 页面消失恢复系统服务菜单
861      TextMenuController.disableSystemServiceMenuItems(false);
862    }
863
864    build() {
865      Row() {
866        Column() {
867          RichEditor(this.options).onReady(() => {
868            this.controller.addTextSpan("这是一个RichEditor",
869              {
870                style:
871                {
872                  fontSize: 30
873                }
874              })
875          })
876            .height(60)
877            .editMenuOptions({
878              onCreateMenu: (menuItems: Array<TextMenuItem>) => {
879                // menuItems不包含被屏蔽的系统菜单项
880                return menuItems;
881              },
882              onMenuItemClick: (menuItem: TextMenuItem, textRange: TextRange) => {
883                return false;
884              }
885            })
886        }.width('100%')
887      }
888      .height('100%')
889    }
890  }
891  ```
892
893![RichEditor_disable_system_service_menuItems](figures/RichEditor_disable_system_service_menuItems.gif)
894
895通过[disableMenuItems](../reference/apis-arkui/arkts-apis-uicontext-textmenucontroller.md#disablemenuitems20)可以屏蔽富文本选择菜单内指定的系统服务菜单项。
896
897此接口可精确屏蔽指定的系统服务菜单项,保留应用所需的系统菜单功能,使菜单更贴合实际交互设计。
898
899  ```ts
900  import { TextMenuController } from '@kit.ArkUI';
901
902  // xxx.ets
903  @Entry
904  @Component
905  struct Index {
906    controller: RichEditorController = new RichEditorController();
907    options: RichEditorOptions = { controller: this.controller };
908
909    aboutToAppear(): void {
910      // 禁用搜索和翻译菜单
911      TextMenuController.disableMenuItems([TextMenuItemId.SEARCH, TextMenuItemId.TRANSLATE])
912    }
913
914    aboutToDisappear(): void {
915      // 恢复系统服务菜单
916      TextMenuController.disableMenuItems([])
917    }
918
919    build() {
920      Row() {
921        Column() {
922          RichEditor(this.options).onReady(() => {
923            this.controller.addTextSpan("这是一个RichEditor",
924              {
925                style:
926                {
927                  fontSize: 30
928                }
929              })
930          })
931            .height(60)
932            .editMenuOptions({
933              onCreateMenu: (menuItems: Array<TextMenuItem>) => {
934                // menuItems不包含搜索和翻译
935                return menuItems;
936              },
937              onMenuItemClick: (menuItem: TextMenuItem, textRange: TextRange) => {
938                return false
939              }
940            })
941        }.width('100%')
942      }
943      .height('100%')
944    }
945  }
946  ```
947
948  ![alt text](figures/richEditor_disable_menuItems.gif)
949
950### 设置自定义选择菜单
951
952通过[bindSelectionMenu](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#bindselectionmenu)设置自定义选择菜单。
953
954组件原本具有默认的文本选择菜单,包含复制、剪切和全选的功能。用户可使用该属性设定自定义菜单,例如翻译英文、加粗字体等丰富的菜单功能。
955
956当自定义菜单超长时,建议内部嵌套Scroll组件使用,避免键盘被遮挡。
957
958```ts
959controller: RichEditorController = new RichEditorController();
960options: RichEditorOptions = { controller: this.controller };
961
962RichEditor(this.options)
963  .onReady(() => {
964    this.controller.addTextSpan('组件设置了自定义菜单,长按可触发。', {
965      style: {
966        fontColor: Color.Black,
967        fontSize: 18
968      }
969    })
970  })
971  .bindSelectionMenu(RichEditorSpanType.TEXT, this.SystemMenu, ResponseType.LongPress, {
972    onDisappear: () => {
973      this.sliderShow = false
974    }
975  })
976// 绑定自定义菜单
977  .width(300)
978  .height(300)
979
980@Builder
981SystemMenu() {
982  Column() {
983    Menu() {
984      if (this.controller) {
985        MenuItemGroup() {
986          MenuItem({
987            startIcon: $r("sys.media.ohos_ic_public_cut"),
988            content: "剪切",
989            labelInfo: "Ctrl+X"
990          })
991          MenuItem({
992            startIcon: $r("sys.media.ohos_ic_public_copy"),
993            content: "复制",
994            labelInfo: "Ctrl+C"
995          })
996          MenuItem({
997            startIcon: $r("sys.media.ohos_ic_public_paste"),
998            content: "粘贴",
999            labelInfo: "Ctrl+V"
1000          })
1001        }
1002      }
1003    }
1004    .radius(this.theme.containerBorderRadius)
1005    .clip(true)
1006    .backgroundColor(Color.White)
1007    .width(this.theme.defaultMenuWidth)
1008  }
1009  .width(this.theme.defaultMenuWidth)
1010}
1011```
1012
1013![alt text](figures/richeditor_image_bindselectionmenu.gif)
1014
1015## 布局配置
1016
1017组件支持通过接口配置布局规则,开发者可以根据业务场景定制合适的布局规则。
1018
1019### 设置最大行数
1020
1021通过[maxLines](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#maxlines18)可以设置富文本组件内可显示文本的最大行数。
1022
1023此接口控制组件内文本的显示范围,防止文本过长影响页面布局,确保不同设备和场景下的文本显示效果一致,提升界面兼容性和美观度。
1024
1025```ts
1026controller: RichEditorController = new RichEditorController();
1027options: RichEditorOptions = { controller: this.controller };
1028
1029RichEditor(this.options)
1030  .onReady(() => {
1031    this.controller.addTextSpan('组件设置了最大行数\n超出内容将会以滚动显示\n超出1行\n超出2行\n超出3行\n超出4行', {
1032      style: {
1033        fontColor: Color.Black,
1034        fontSize: 15
1035      }
1036    })
1037  })
1038  .maxLines(2)
1039```
1040
1041![max lines](figures/RichEditor_maxLines.gif)
1042
1043## 样式设置
1044
1045组件支持对内容设置复杂的样式。
1046
1047### 设置用户预设的文本样式
1048
1049通过[setTypingStyle](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#settypingstyle11)可以设置用户预设的文本样式。
1050
1051此接口可用于个性化的写作体验,例如可以使用此接口让输入的不同层级标题自动应用相应格式(如一级、二级标题)。
1052
1053```ts
1054controller: RichEditorController = new RichEditorController();
1055options: RichEditorOptions = { controller: this.controller };
1056
1057RichEditor(this.options)
1058  .onReady(() => {
1059    this.controller.addTextSpan('点击按钮,改变预设文本样式。', {
1060      style: {
1061        fontColor: Color.Black,
1062        fontSize: 15
1063      }
1064    })
1065  })
1066  .width(300)
1067  .height(60)
1068Button('setTypingStyle', {
1069  buttonStyle: ButtonStyleMode.NORMAL
1070})
1071  .height(30)
1072  .fontSize(13)
1073  .onClick(() => {
1074    this.controller.setTypingStyle({
1075      fontWeight: 'medium',
1076      fontColor: Color.Pink,
1077      fontSize: 15,
1078      fontStyle: FontStyle.Italic,
1079      decoration: {
1080        type: TextDecorationType.Underline,
1081        color: Color.Gray
1082      }
1083    })
1084  })
1085```
1086
1087![alt text](figures/richeditor_image_setTypingStyle.gif)
1088
1089### 设置装饰线
1090
1091通过[decoration](../reference/apis-arkui/arkui-ts/ts-basic-components-span.md#decoration)设置富文本组件中文本装饰线的样式、颜色和粗细。
1092
1093设置文本装饰线可突出关键信息、区分文本状态、增强视觉层次。例如,为重要标题或关键词添加装饰线,帮助用户快速获取信息。
1094
1095  ```ts
1096  private controller: RichEditorController = new RichEditorController();
1097  RichEditor({ controller: this.controller })
1098    .onReady(() => {
1099      this.controller.addTextSpan('一段预置的文本', {
1100        style: {
1101          fontSize: 25,
1102          decoration: {
1103            type: TextDecorationType.LineThrough,
1104            color: Color.Blue,
1105            // 设置装饰线粗细比例为6
1106            thicknessScale: 6
1107          }
1108        }
1109      })
1110    })
1111  ```
1112
1113![RichEditor_decoration](figures/RichEditor_decoration.jpg)
1114
1115通过[DecorationOptions](../reference/apis-arkui/arkui-ts/ts-universal-styled-string.md#decorationoptions20)中的enableMultiType设置多装饰线,比如同时设置下划线和中划线。
1116
1117此接口适用于复杂业务场景,满足文本装饰的多样化需求。在文档协作过程中,多人编辑时,可以通过使用不同的装饰线组合来区分文本状态,从而提高协作效率。
1118
1119  ```ts
1120  RichEditor({ controller: this.styledStringController })
1121  Button('多装饰线文本')
1122    .fontSize(20)
1123    .onClick(() => {
1124      let mutString: MutableStyledString = new MutableStyledString('设置富文本多装饰线', [
1125        {
1126          start: 0,
1127          length: 9,
1128          styledKey: StyledStringKey.FONT,
1129          styledValue: new TextStyle({ fontSize: LengthMetrics.vp(25) })
1130        },
1131        {
1132          start: 0,
1133          length: 5,
1134          styledKey: StyledStringKey.DECORATION,
1135          styledValue: new DecorationStyle(
1136            {
1137              type: TextDecorationType.Underline,
1138            },
1139            {
1140              // 开启多装饰线
1141              enableMultiType: true
1142            }
1143          )
1144        },
1145        {
1146          start: 2,
1147          length: 4,
1148          styledKey: StyledStringKey.DECORATION,
1149          styledValue: new DecorationStyle(
1150           {
1151              type: TextDecorationType.LineThrough,
1152            },
1153            {
1154              // 开启多装饰线
1155              enableMultiType: true
1156            }
1157          )
1158        },
1159      ])
1160      this.styledStringController.setStyledString(mutString);
1161    })
1162  ```
1163
1164![RichEditor_decoration_multi_type](figures/RichEditor_decoration_multi_type.jpg)
1165
1166### 设置垂直居中
1167
1168通过[textVerticalAlign](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md#textverticalalign20)设置文本段落在垂直方向的对齐方式。
1169
1170此接口优化多元素排版,使组件内容与图片、图标等在垂直方向对齐时,整体布局更协调。
1171
1172  ```ts
1173  controller: RichEditorController = new RichEditorController();
1174  options: RichEditorOptions = { controller: this.controller };
1175
1176  Column({ space: 5 }) {
1177    RichEditor(this.options)
1178      .onReady(() => {
1179        this.controller.addImageSpan($r('app.media.startIcon'), {
1180          imageStyle: {
1181            size: [100, 100]
1182          }
1183        })
1184        this.controller.addTextSpan("这是一段富文本,展示了文本垂直居中的效果。", {
1185          style: {
1186            fontColor: Color.Pink,
1187            fontSize: "32"
1188          },
1189          paragraphStyle: {
1190            textAlign: TextAlign.Start,
1191            textVerticalAlign: TextVerticalAlign.CENTER,
1192            leadingMargin: 16
1193          }
1194        })
1195      })
1196  }
1197  ```
1198
1199![RichEditor_text_vertical_align](figures/RichEditor_text_vertical_align.jpg)
1200
1201### 设置中西文自动间距
1202
1203通过[enableAutoSpacing](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md#enableautospacing20)设置是否开启中文与西文的自动间距。
1204
1205此接口优化文本排版,提升组件内文本的可读性。设置自动间距后,中文与西文间产生适当空隙,便于区分不同语种,减少视觉干扰。
1206
1207```ts
1208Column() {
1209  RichEditor(this.options)
1210    .onReady(() => {
1211      this.controller.addTextSpan("中西文Auto Spacing自动间距",
1212        {
1213          style:
1214          {
1215            fontColor: Color.Orange,
1216            fontSize: 20
1217          }
1218        })
1219    })
1220    .enableAutoSpacing(this.enableAutoSpace)
1221}
1222```
1223
1224![RichEditor_enable_auto_spacing](figures/RichEditor_enable_auto_spacinge.gif)
1225