• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# RichEditor
2
3支持图文混排和文本交互式编辑的组件。
4
5>  **说明:**
6>
7>  该组件从API Version 10开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
8>
9>  RichEditor[仅支持通过onDragStart事件](ts-universal-events-drag-drop.md)实现浮起等拖拽效果。
10
11
12## 子组件
13
14不包含子组件。
15
16
17## 接口
18
19RichEditor(value: RichEditorOptions)
20
21**参数:**
22
23| 参数名   | 参数类型                                    | 必填   | 参数描述        |
24| ----- | --------------------------------------- | ---- | ----------- |
25| value | [RichEditorOptions](#richeditoroptions) | 是    | 富文本组件初始化选项。 |
26
27
28## 属性
29
30除支持[通用属性](ts-universal-attributes-size.md)外,还支持以下属性:
31
32>  **说明:**
33>
34>  其中clip属性默认值为true。
35>  align属性只支持上方丶中间和下方位置的对齐方式。
36
37| 名称                               | 参数类型                                     | 描述                                       |
38| -------------------------------- | ---------------------------------------- | ---------------------------------------- |
39| customKeyboard                   | [CustomBuilder](ts-types.md#custombuilder8) | 设置自定义键盘。<br/>**说明:**<br/>当设置自定义键盘时,输入框激活后不会打开系统输入法,而是加载指定的自定义组件。<br/>自定义键盘的高度可以通过自定义组件根节点的height属性设置,宽度不可设置,使用系统默认值。<br/>自定义键盘采用覆盖原始界面的方式呈现,不会对应用原始界面产生压缩或者上提。<br/>自定义键盘无法获取焦点,但是会拦截手势事件。<br/>默认在输入控件失去焦点时,关闭自定义键盘。<br/>如果设备支持拍摄输入,设置自定义键盘后,该输入框会不支持拍摄输入。 |
40| bindSelectionMenu                | {<br/>spantype:&nbsp;[RichEditorSpanType](#richeditorspantype),<br/>content:&nbsp;[CustomBuilder](ts-types.md#custombuilder8),<br/>responseType:&nbsp;[ResponseType](ts-appendix-enums.md#responsetype8)&nbsp;\| [RichEditorResponseType<sup>11+</sup>](ts-appendix-enums.md#richeditorresponsetype11),<br/>options?:&nbsp;[SelectionMenuOptions](#selectionmenuoptions11)<br/>} | 设置自定义选择菜单。<br/> 默认值:{<br/>  spanType:&nbsp;RichEditorSpanType.TEXT<br/>responseType:&nbsp;ResponseType.LongPress<br/>其他:空<br/>}<br/>自定义菜单超长时,建议内部嵌套[Scroll](./ts-container-scroll.md)组件使用,避免键盘被遮挡。 |
41| copyOptions                      | [CopyOptions](ts-appendix-enums.md#copyoptions9) | 组件支持设置文本内容是否可复制粘贴。<br />默认值:CopyOptions.LocalDevice <br/>**说明:** <br/>copyOptions不为CopyOptions.None时,长按组件内容,会弹出文本选择弹框。如果通过bindSelectionMenu等方式自定义文本选择菜单,则会弹出自定义的菜单。<br/>设置copyOptions为CopyOptions.None,复制、剪切功能不生效。 |
42| enableDataDetector<sup>11+</sup> | boolean                                  | 使能文本识别。<br/>默认值: false<br/>**说明:**<br/>所识别实体的`fontColor`和`decoration`会被更改为如下样式:<br/>fontColor:Color.Blue<br/>decoration:&nbsp;{<br/>type:&nbsp;TextDecorationType.Underline,<br/>color:&nbsp;Color.Blue<br/>}<br/>该接口依赖设备底层应具有文本识别能力,否则设置不会生效。<br/>当`enableDataDetector`设置为true,同时不设置`dataDetectorConfig`属性时,默认识别所有类型的实体。<br/>当`copyOptions`设置为CopyOptions.None时,该功能不会生效。<br/>对`addBuilderSpan`的节点文本,该功能不会生效。 |
43| dataDetectorConfig<sup>11+</sup> | [TextDataDetectorConfig](#textdatadetectorconfig11) | 文本识别配置。 <br/>默认值:{<br/>types:&nbsp;[ ],<br/>onDetectResultUpdate:&nbsp;null<br/>} <br />**说明:**<br/>需配合`enableDataDetector`一起使用,设置`enableDataDetector`为true时,`dataDetectorConfig`的配置才能生效。<br/> |
44
45## 事件
46
47除支持[通用事件](ts-universal-events-click.md)外,还支持以下事件:
48
49| 名称                                       | 功能描述                                     |
50| ---------------------------------------- | ---------------------------------------- |
51| onReady(callback:&nbsp;()&nbsp;=&gt;&nbsp;void) | 富文本组件初始化完成后,触发回调。                        |
52| onSelect(callback:&nbsp;(value:&nbsp;[RichEditorSelection](#richeditorselection))&nbsp;=&gt;&nbsp;void) | 鼠标左键按下选择,松开左键后触发回调。<br />用手指选择时,松开手指触发回调。 <br />- value:选中的所有span信息。 |
53| aboutToIMEInput(callback:&nbsp;(value:&nbsp;[RichEditorInsertValue](#richeditorinsertvalue))&nbsp;=&gt;&nbsp;boolean) | 输入法输入内容前,触发回调。<br />- value:输入法将要输入内容信息。 |
54| onIMEInputComplete(callback:&nbsp;(value:&nbsp;[RichEditorTextSpanResult](#richeditortextspanresult))&nbsp;=&gt;&nbsp;void) | 输入法完成输入后,触发回调。<br />- value:输入法完成输入后的文本Span信息。 |
55| aboutToDelete(callback:&nbsp;(value:&nbsp;[RichEditorDeleteValue](#richeditordeletevalue))&nbsp;=&gt;&nbsp;boolean) | 输入法删除内容前,触发回调。 <br />- value:准备删除的内容所在的文本Span信息。 |
56| onDeleteComplete(callback:&nbsp;()&nbsp;=&gt;&nbsp;void) | 输入法完成删除后,触发回调。                           |
57| onPaste<sup>11+</sup>(callback: (event?: [PasteEvent](#pasteevent11)) => void) | 完成粘贴前,触发回调。 <br/>**说明:** <br/>系统的默认粘贴和拖拽行为,只支持纯文本的粘贴。<br/>开发者可以通过该方法,覆盖系统默认行为,实现图文的粘贴。 |
58
59## RichEditorInsertValue
60
61插入文本信息。
62
63| 名称           | 类型     | 必填   | 说明         |
64| ------------ | ------ | ---- | ---------- |
65| insertOffset | number | 是    | 插入的文本偏移位置。 |
66| insertValue  | string | 是    | 插入的文本内容。   |
67
68
69## RichEditorDeleteValue
70
71| 名称                    | 类型                                       | 必填   | 说明                  |
72| --------------------- | ---------------------------------------- | ---- | ------------------- |
73| offset                | number                                   | 是    | 删除内容的偏移位置。          |
74| direction             | [RichEditorDeleteDirection](#richeditordeletedirection) | 是    | 删除操作的方向。            |
75| length                | number                                   | 是    | 删除内容长度。             |
76| richEditorDeleteSpans | Array<[RichEditorTextSpanResult](#richeditortextspanresult) \| [RichEditorImageSpanResult](#richeditorimagespanresult)> | 是    | 删除的文本或者图片Span的具体信息。 |
77
78
79## RichEditorDeleteDirection
80
81删除操作的方向。
82
83| 名称       | 描述    |
84| -------- | ----- |
85| BACKWARD | 向后删除。 |
86| FORWARD  | 向前删除。 |
87
88
89## RichEditorTextSpanResult
90
91文本Span信息。
92
93| 名称                            | 类型                                       | 必填   | 说明                     |
94| ----------------------------- | ---------------------------------------- | ---- | ---------------------- |
95| spanPosition                  | [RichEditorSpanPosition](#richeditorspanposition) | 是    | Span位置。                |
96| value                         | string                                   | 是    | 文本Span内容。              |
97| textStyle                     | [RichEditorTextStyleResult](#richeditortextstyleresult) | 是    | 文本Span样式信息。            |
98| offsetInSpan                  | [number, number]                         | 是    | 文本Span内容里有效内容的起始和结束位置。 |
99| valueResource<sup>11+</sup>   | [Resource](ts-types.md#resource)         | 否    | 组件SymbolSpan内容。        |
100| SymbolSpanStyle<sup>11+</sup> | [RichEditorSymbolSpanStyle](#richeditorsymbolspanstyle11) | 否    | 组件SymbolSpan样式信息。      |
101
102
103## RichEditorSpanPosition
104
105Span位置信息。
106
107| 名称        | 类型               | 必填   | 说明                          |
108| --------- | ---------------- | ---- | --------------------------- |
109| spanIndex | number           | 是    | Span索引值。                    |
110| spanRange | [number, number] | 是    | Span内容在RichEditor内的起始和结束位置。 |
111
112## RichEditorSpanType
113
114Span类型信息。
115
116| 名称    | 值     | 描述           |
117| ----- | ---- | ------------ |
118| TEXT  | 0 | Span为文字类型。   |
119| IMAGE | 1 | Span为图像类型。   |
120| MIXED | 2 | Span为图文混合类型。 |
121
122## RichEditorTextStyleResult
123
124后端返回的文本样式信息。
125
126| 名称         | 类型                                       | 必填   | 描述           |
127| ---------- | ---------------------------------------- | ---- | ------------ |
128| fontColor  | [ResourceColor](ts-types.md#resourcecolor) | 是    | 文本颜色。        |
129| fontSize   | number                                   | 是    | 字体大小。        |
130| fontStyle  | [FontStyle](ts-appendix-enums.md#fontstyle) | 是    | 字体样式。        |
131| fontWeight | number                                   | 是    | 字体粗细。        |
132| fontFamily | string                                   | 是    | 字体列表。        |
133| decoration | {<br/>type:&nbsp;[TextDecorationType](ts-appendix-enums.md#textdecorationtype),<br/>color:&nbsp;[ResourceColor](ts-types.md#resourcecolor)<br/>} | 是    | 文本装饰线样式及其颜色。 |
134
135## RichEditorImageSpanResult
136
137后端返回的图片信息。
138
139| 名称               | 类型                                                                | 必填  | 描述               |
140|------------------|-------------------------------------------------------------------|-----|------------------|
141| spanPosition     | [RichEditorSpanPosition](#richeditorspanposition)                 | 是   | Span位置。          |
142| valuePixelMap    | [PixelMap](../../apis-image-kit/js-apis-image.md#pixelmap7)                    | 否   | 图片内容。            |
143| valueResourceStr | [ResourceStr](ts-types.md#resourcestr)                            | 否   | 图片资源id。          |
144| imageStyle       | [RichEditorImageSpanStyleResult](#richeditorimagespanstyleresult) | 是   | 图片样式。            |
145| offsetInSpan     | [number, number]                                                  | 是   | Span里图片的起始和结束位置。 |
146
147## RichEditorImageSpanStyleResult
148
149后端返回的图片样式信息。
150
151| 名称            | 类型                                       | 必填   | 描述        |
152| ------------- | ---------------------------------------- | ---- | --------- |
153| size          | [number, number]                         | 是    | 图片的宽度和高度。 |
154| verticalAlign | [ImageSpanAlignment](ts-basic-components-imagespan.md#imagespanalignment) | 是    | 图片垂直对齐方式。 |
155| objectFit     | [ImageFit](ts-appendix-enums.md#imagefit) | 是    | 图片缩放类型。   |
156
157## RichEditorOptions
158
159RichEditor初始化参数。
160
161| 名称         | 类型                                       | 必填   | 说明      |
162| ---------- | ---------------------------------------- | ---- | ------- |
163| controller | [RichEditorController](#richeditorcontroller) | 是    | 富文本控制器。 |
164
165## TextDataDetectorConfig<sup>11+</sup>
166
167文本识别配置参数。
168
169| 名称         | 类型                                                                    | 必填   | 说明      |
170| ---------- |-----------------------------------------------------------------------| ---- | ------- |
171| types | [TextDataDetectorType](ts-appendix-enums.md#textdatadetectortype11)[] | 是    | 文本识别的实体类型。设置`types`为`null`或者`[]`时,识别所有类型的实体,否则只识别指定类型的实体。 |
172| onDetectResultUpdate | (result:&nbsp;string)&nbsp;=&gt;&nbsp;void                            | 否    | 文本识别成功后,触发`onDetectResultUpdate`回调。<br/>`result`:文本识别的结果,Json格式。 |
173
174## RichEditorController
175
176RichEditor组件的控制器。
177
178### 导入对象
179
180```
181controller: RichEditorController = new RichEditorController()
182```
183
184### getCaretOffset
185
186getCaretOffset(): number
187
188返回当前光标所在位置。
189
190**返回值:**
191
192| 类型     | 说明        |
193| ------ | --------- |
194| number | 当前光标所在位置。 |
195
196### setCaretOffset
197
198setCaretOffset(offset: number): boolean
199
200设置光标位置。
201
202**参数:**
203
204| 参数名    | 参数类型   | 必填   | 参数描述                 |
205| ------ | ------ | ---- | -------------------- |
206| offset | number | 是    | 光标偏移位置。超出文本范围时,设置失败。 |
207
208**返回值:**
209
210| 类型      | 说明        |
211| ------- | --------- |
212| boolean | 光标是否设置成功。 |
213
214### addTextSpan
215
216addTextSpan(value: string, options?: RichEditorTextSpanOptions): number
217
218添加文本内容。
219
220**参数:**
221
222| 参数名     | 参数类型                                     | 必填   | 参数描述  |
223| ------- | ---------------------------------------- | ---- | ----- |
224| value   | string                                   | 是    | 文本内容。 |
225| options | [RichEditorTextSpanOptions](#richeditortextspanoptions) | 否    | 文本选项。 |
226
227**返回值:**
228
229| 类型     | 说明                   |
230| ------ | -------------------- |
231| number | 添加完成的Text Span所在的位置。 |
232
233### addImageSpan
234
235addImageSpan(value: PixelMap | ResourceStr, options?: RichEditorImageSpanOptions): number
236
237添加图片内容。
238
239**参数:**
240
241| 参数名     | 参数类型                                     | 必填   | 参数描述  |
242| ------- | ---------------------------------------- | ---- | ----- |
243| value   | [PixelMap](../../apis-image-kit/js-apis-image.md#pixelmap7)\|[ResourceStr](ts-types.md#resourcestr) | 是    | 图片内容。 |
244| options | [RichEditorImageSpanOptions](#richeditorimagespanoptions) | 否    | 图片选项。 |
245
246**返回值:**
247
248| 类型     | 说明                   |
249| ------ | -------------------- |
250| number | 添加完成的imageSpan所在的位置。 |
251
252### addBuilderSpan<sup>11+</sup>
253
254addBuilderSpan(value: CustomBuilder, options?: RichEditorBuilderSpanOptions): number
255
256> **说明:**
257>
258> - RichEditor组件添加占位Span,占位Span调用系统的measure方法计算真实的长宽和位置。
259> - 可通过[RichEditorBuilderSpanOptions](#richeditorbuilderspanoptions11)设置此builder在RichEditor中的index(一个文字为一个单位)。
260> - 此占位Span不可获焦,不支持拖拽(与可拖拽Span如文本或图片Span一起拖拽时,此Span对应的位置内容不会显示但会占相同大小的区域),支持部分通用属性,占位、删除等能力等同于ImageSpan,长度视为一个文字。
261> - 不支持通过[bindSelectionMenu](#属性)设置自定义菜单。
262> - 不支持通过[getSpans](#getspans),[getSelection](#getselection11),[onSelect](#事件),[aboutToDelete](#事件)获取builderSpan信息。
263> - 不支持通过[updateSpanStyle](#updatespanstyle),[updateParagraphStyle](#updateparagraphstyle11)等方式更新builder。
264> - 对此builder节点进行复制或粘贴不生效。
265> - builder的布局约束由RichEditor传入,如果builder里最外层组件不设置大小,则会用RichEditor的大小作为maxSize。
266> - builder的手势相关事件机制与通用手势事件相同,如果builder中未设置透传,则仅有builder中的子组件响应。
267
268通用属性仅支持[size](ts-universal-attributes-size.md#size)、[padding](ts-universal-attributes-size.md#padding)、[margin](ts-universal-attributes-size.md#margin)、[aspectRatio](ts-universal-attributes-layout-constraints.md#aspectratio)、[borderStyle](ts-universal-attributes-border.md#borderstyle)、[borderWidth](ts-universal-attributes-border.md#borderwidth)、[borderColor](ts-universal-attributes-border.md#bordercolor)、[borderRadius](ts-universal-attributes-border.md#borderradius)、[backgroundColor](ts-universal-attributes-background.md#backgroundcolor)、[backgroundBlurStyle](ts-universal-attributes-background.md#backgroundblurstyle9)、[opacity](ts-universal-attributes-opacity.md)、[blur](ts-universal-attributes-image-effect.md#blur)、[backdropBlur](ts-universal-attributes-image-effect.md#backdropblur)、[shadow](ts-universal-attributes-image-effect.md#shadow)、[grayscale](ts-universal-attributes-image-effect.md#grayscale)、[brightness](ts-universal-attributes-image-effect.md#brightness)、[saturate](ts-universal-attributes-image-effect.md#saturate)、
269[contrast](ts-universal-attributes-image-effect.md#contrast)、[invert](ts-universal-attributes-image-effect.md#invert)、[sepia](ts-universal-attributes-image-effect.md#sepia)、[hueRotate](ts-universal-attributes-image-effect.md#huerotate)、[colorBlend](ts-universal-attributes-image-effect.md#colorblend7)、[linearGradientBlur](ts-universal-attributes-image-effect.md#lineargradientblur10)、[clip](ts-universal-attributes-sharp-clipping.md#clip)、[mask](ts-universal-attributes-sharp-clipping.md#mask)、[foregroundBlurStyle](ts-universal-attributes-foreground-blur-style.md#foregroundblurstyle)、[accessibilityGroup](ts-universal-attributes-accessibility.md#accessibilitygroup)、[accessibilityText](ts-universal-attributes-accessibility.md#accessibilitytext)、[accessibilityDescription](ts-universal-attributes-accessibility.md#accessibilitydescription)、[accessibilityLevel](ts-universal-attributes-accessibility.md#accessibilitylevel)、[sphericalEffect](ts-universal-attributes-image-effect-sys.md#sphericaleffect10)、[lightUpEffect](ts-universal-attributes-image-effect-sys.md#lightupeffect10)、[pixelStretchEffect](ts-universal-attributes-image-effect-sys.md#pixelstretcheffect10)。
270
271**参数:**
272
273| 参数名     | 参数类型                                     | 必填   | 参数描述       |
274| ------- | ---------------------------------------- | ---- | ---------- |
275| value   | [CustomBuilder](ts-types.md#custombuilder8) | 是    | 自定义组件。     |
276| options | [RichEditorBuilderSpanOptions](#richeditorbuilderspanoptions11) | 否    | builder选项。 |
277
278**返回值:**
279
280| 类型     | 说明                     |
281| ------ | ---------------------- |
282| number | 添加完成的builderSpan所在的位置。 |
283
284### addSymbolSpan<sup>11+</sup>
285
286addSymbolSpan(value: Resource, options?: RichEditorSymbolSpanOptions ): number
287
288在Richeditor中添加SymbolSpan。
289
290暂不支持手势处理。
291
292**参数:**
293
294| 参数名     | 参数类型                                     | 必填   | 参数描述  |
295| ------- | ---------------------------------------- | ---- | ----- |
296| value   | [Resource](ts-types.md#resource)         | 是    | 组件内容。 |
297| options | [RichEditorSymbolSpanOptions](#richeditorsymbolspanoptions11) | 否    | 组件选项。 |
298
299**返回值:**
300
301| 类型     | 说明                    |
302| ------ | --------------------- |
303| number | 添加完成的SymbolSpan所在的位置。 |
304
305### getTypingStyle<sup>11+</sup>
306
307getTypingStyle(): RichEditorTextStyle
308
309获得用户预设的样式。
310
311**返回值:**
312
313| 类型                                       | 说明      |
314| ---------------------------------------- | ------- |
315| [RichEditorTextStyle](#richeditortextstyle) | 用户预设样式。 |
316
317### setTypingStyle<sup>11+</sup>
318
319setTypingStyle(value: RichEditorTextStyle): void
320
321设置用户预设的样式。
322
323**参数:**
324
325| 参数名   | 参数类型                                     | 必填   | 参数描述  |
326| ----- | ---------------------------------------- | ---- | ----- |
327| value | [RichEditorTextStyle](#richeditortextstyle) | 是    | 预设样式。 |
328
329### updateSpanStyle
330
331updateSpanStyle(value: RichEditorUpdateTextSpanStyleOptions | RichEditorUpdateImageSpanStyleOptions | RichEditorUpdateSymbolSpanStyleOptions): void
332
333更新文本或者图片样式。<br/>若只更新了一个Span的部分内容,则会根据更新部分、未更新部分将该Span拆分为多个Span。
334
335使用该接口更新文本或图片样式时默认不会关闭自定义文本选择菜单。
336
337**参数:**
338
339| 名称 | 类型 | 必填 | 描述                               |
340| ------ | -------- | ---- | -------------------------------------- |
341| value | [RichEditorUpdateTextSpanStyleOptions](#richeditorupdatetextspanstyleoptions) \| [RichEditorUpdateImageSpanStyleOptions](#richeditorupdateimagespanstyleoptions) \| [RichEditorUpdateSymbolSpanStyleOptions](#richeditorupdatesymbolspanstyleoptions11)<sup>11+</sup> | 是 | 文本或者图片的样式选项信息。 |
342
343### updateParagraphStyle<sup>11+</sup>
344
345updateParagraphStyle(value: RichEditorParagraphStyleOptions): void
346
347更新段落的样式。
348
349**参数:**
350
351| 名称    | 类型                                       | 必填   | 描述         |
352| ----- | ---------------------------------------- | ---- | ---------- |
353| value | [RichEditorParagraphStyleOptions](#richeditorparagraphstyleoptions11) | 是    | 段落的样式选项信息。 |
354
355### getSpans
356
357getSpans(value?: RichEditorRange): Array<RichEditorTextSpanResult| RichEditorImageSpanResult>
358
359获取span信息。
360
361**参数:**
362
363| 参数名   | 参数类型                                | 必填   | 参数描述        |
364| ----- | ----------------------------------- | ---- | ----------- |
365| value | [RichEditorRange](#richeditorrange) | 否    | 需要获取span范围。 |
366
367**返回值:**
368
369| 类型                                       | 说明           |
370| ---------------------------------------- | ------------ |
371| Array<[RichEditorTextSpanResult](#richeditortextspanresult) \| [RichEditorImageSpanResult](#richeditorimagespanresult)> | 文本和图片Span信息。 |
372
373### deleteSpans
374
375deleteSpans(value?: RichEditorRange): void
376
377删除指定范围内的文本和图片。
378
379**参数:**
380
381| 参数名   | 参数类型                                | 必填   | 参数描述                |
382| ----- | ----------------------------------- | ---- | ------------------- |
383| value | [RichEditorRange](#richeditorrange) | 否    | 删除范围。省略时,删除所有文本和图片。 |
384
385### getParagraphs<sup>11+</sup>
386
387getParagraphs(value?: RichEditorRange): Array\<RichEditorParagraphResult>
388
389获得指定返回的段落。
390
391**参数:**
392
393| 参数名   | 参数类型                                | 必填   | 参数描述       |
394| ----- | ----------------------------------- | ---- | ---------- |
395| value | [RichEditorRange](#richeditorrange) | 否    | 需要获取段落的范围。 |
396
397**返回值:**
398
399| 类型                                       | 说明       |
400| ---------------------------------------- | -------- |
401| Array\<[RichEditorParagraphResult](#richeditorparagraphresult11)> | 选中段落的信息。 |
402
403### closeSelectionMenu
404
405closeSelectionMenu(): void
406
407关闭自定义选择菜单或系统默认选择菜单。
408
409### setSelection<sup>11+</sup>
410
411setSelection(selectionStart:&nbsp;number, selectionEnd:&nbsp;number)
412
413支持设置文本选中,选中部分背板高亮。
414
415selectionStart和selectionEnd均为-1时表示全选。
416
417接口调用前有带手柄菜单弹出时则调用后不主动关闭菜单,且调整菜单位置。
418
419接口调用前有不带手柄菜单弹出时则调用后不主动关闭菜单,且保持菜单原来位置。
420
421接口调用前无菜单弹出,则调用后也无菜单弹出。
422
423未获焦时调用该接口不产生选中效果。
424
425使用[示例](ts-composite-components-selectionmenu.md#示例)。
426
427**参数:**
428
429| 参数名            | 参数类型   | 必填   | 参数描述    |
430| -------------- | ------ | ---- | ------- |
431| selectionStart | number | 是    | 选中开始位置。 |
432| selectionEnd   | number | 是    | 选中结束位置。 |
433
434### getSelection<sup>11+</sup>
435
436getSelection(): RichEditorSelection
437
438获取选中文本内容。如果未选中内容,返回光标所在span信息。
439
440使用[示例](ts-composite-components-selectionmenu.md#示例)。
441
442**返回值:**
443
444| 类型                                       | 说明      |
445| ---------------------------------------- | ------- |
446| [RichEditorSelection](#richeditorselection) | 选中内容信息。 |
447
448## RichEditorSelection
449
450选中内容信息。
451
452| 名称        | 类型                                       | 必填   | 说明      |
453| --------- | ---------------------------------------- | ---- | ------- |
454| selection | [number, number]                         | 是    | 选中范围。   |
455| spans     | Array<[RichEditorTextSpanResult](#richeditortextspanresult)\| [RichEditorImageSpanResult](#richeditorimagespanresult)> | 是    | span信息。 |
456
457
458## RichEditorUpdateTextSpanStyleOptions
459
460文本样式选项。
461
462| 名称        | 类型                                       | 必填   | 描述                              |
463| --------- | ---------------------------------------- | ---- | ------------------------------- |
464| start     | number                                   | 否    | 需要更新样式的文本起始位置,省略或者设置负值时表示从0开始。  |
465| end       | number                                   | 否    | 需要更新样式的文本结束位置,省略或者超出文本范围时表示无穷大。 |
466| textStyle | [RichEditorTextStyle](#richeditortextstyle) | 是    | 文本样式。                           |
467
468>  **说明:**
469>
470>  当start大于end时为异常情况,此时start为0,end为无穷大。
471
472## RichEditorUpdateImageSpanStyleOptions
473
474图片样式选项。
475
476| 名称         | 类型                                       | 必填   | 描述                              |
477| ---------- | ---------------------------------------- | ---- | ------------------------------- |
478| start      | number                                   | 否    | 需要更新样式的图片起始位置,省略或者设置负值时表示从0开始。  |
479| end        | number                                   | 否    | 需要更新样式的图片结束位置,省略或者超出文本范围时表示无穷大。 |
480| imageStyle | [RichEditorImageSpanStyle](#richeditorimagespanstyle) | 是    | 图片样式。                           |
481
482>  **说明:**
483>
484>  当start大于end时为异常情况,此时start为0,end为无穷大。
485
486## RichEditorUpdateSymbolSpanStyleOptions<sup>11+</sup>
487
488SymbolSpan样式选项。
489
490| 名称          | 类型                                       | 必填   | 描述                              |
491| ----------- | ---------------------------------------- | ---- | ------------------------------- |
492| start       | number                                   | 否    | 需要更新样式的文本起始位置,省略或者设置负值时表示从0开始。  |
493| end         | number                                   | 否    | 需要更新样式的文本结束位置,省略或者超出文本范围时表示无穷大。 |
494| symbolStyle | [RichEditorSymbolSpanStyle](#richeditorsymbolspanstyle11) | 是    | 组件样式。                           |
495
496>  **说明:**
497>
498>  当start大于end时为异常情况,此时start为0,end为无穷大。
499
500## RichEditorParagraphStyleOptions<sup>11+</sup>
501
502段落样式选项
503
504| 名称    | 类型                                       | 必填   | 描述                                 |
505| ----- | ---------------------------------------- | ---- | ---------------------------------- |
506| start | number                                   | 否    | 需要更新样式的段落起始位置,省略或者设置负值时表示从0开始。     |
507| end   | number                                   | 否    | 需要更新样式的段落结束位置,省略、负数或者超出文本范围时表示无穷大。 |
508| style | [RichEditorParagraphStyle](#richeditorparagraphstyle11) | 是    | 段落样式。                              |
509
510>  **说明:**
511>
512>  当start大于end时为异常情况,此时start为0,end为无穷大。
513
514## RichEditorParagraphStyle<sup>11+</sup>
515
516段落样式。
517
518| 名称            | 类型                                       | 必填   | 描述                 |
519| ------------- | ---------------------------------------- | ---- | ------------------ |
520| textAlign     | [TextAlign](ts-appendix-enums.md#textalign) | 否    | 设置文本段落在水平方向的对齐方式。  |
521| leadingMargin | [Dimension](ts-types.md#dimension10) \| [LeadingMarginPlaceholder](#leadingmarginplaceholder11) | 否    | 设置文本段落缩进,不支持设置百分比,图片放在段首时不支持。 |
522
523## LeadingMarginPlaceholder<sup>11+</sup>
524
525前导边距占位符,用于表示文本段落左侧与组件边缘之间的距离。
526
527| 名称       | 类型                                       | 必填   | 描述             |
528| -------- | ---------------------------------------- | ---- | -------------- |
529| pixelMap | [PixelMap](../../apis-image-kit/js-apis-image.md#pixelmap7) | 是    | 图片内容。          |
530| size     | \[[Dimension](ts-types.md#dimension10), [Dimension](ts-types.md#dimension10)\] | 是    | 图片大小,不支持设置百分比。 |
531
532## RichEditorParagraphResult<sup>11+</sup>
533
534后端返回的段落信息。
535
536| 名称    | 类型                                       | 必填   | 描述      |
537| ----- | ---------------------------------------- | ---- | ------- |
538| style | [RichEditorParagraphStyle](#richeditorparagraphstyle11) | 是    | 段落样式。   |
539| range | \[number, number\]                       | 是    | 段落起始和结束位置。 |
540
541## RichEditorTextSpanOptions
542
543添加文本的偏移位置和文本样式信息。
544
545| 名称                           | 类型                                       | 必填   | 描述                         |
546| ---------------------------- | ---------------------------------------- | ---- | -------------------------- |
547| offset                       | number                                   | 否    | 添加文本的位置。省略时,添加到所有文本字符串的最后。<br/>当值小于0时,放在字符串最前面;当值大于字符串长度时,放在字符串最后面。 |
548| style                        | [RichEditorTextStyle](#richeditortextstyle) | 否    | 文本样式信息。省略时,使用系统默认文本信息。     |
549| paragraphStyle<sup>11+</sup> | [RichEditorParagraphStyle](#richeditorparagraphstyle11) | 否    | 段落样式。                      |
550| gesture<sup>11+</sup>        | [RichEditorGesture](#richeditorgesture11) | 否    | 行为触发回调。省略时,仅使用系统默认行为。      |
551
552## RichEditorTextStyle
553
554文本样式信息。
555
556| 名称                       | 类型                                       | 必填   | 描述                                       |
557| ------------------------ | ---------------------------------------- | ---- | ---------------------------------------- |
558| fontColor                | [ResourceColor](ts-types.md#resourcecolor) | 否    | 文本颜色。<br/> 默认值:Color.Black。              |
559| fontSize                 | [Length](ts-types.md#length) \| number            | 否    | 设置字体大小,Length为number类型时,使用fp单位。字体默认大小16。不支持设置百分比字符串。<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。 |
560| fontStyle                | [FontStyle](ts-appendix-enums.md#fontstyle) | 否    | 字体样式。<br/>默认值:FontStyle.Normal。          |
561| fontWeight               | [FontWeight](ts-appendix-enums.md#fontweight) \| number \| string | 否    | 字体粗细。<br/>number类型取值[100,900],取值间隔为100,默认为400,取值越大,字体越粗。<br/>string类型仅支持number类型取值的字符串形式,例如“400”,以及“bold”、“bolder”、“lighter”、“regular” 、“medium”分别对应FontWeight中相应的枚举值。<br/>默认值:FontWeight.Normal。 |
562| fontFamily               | [ResourceStr](ts-types.md#resourcestr) | 否    | 设置字体列表。默认字体'HarmonyOS Sans',当前支持'HarmonyOS Sans'字体和[注册自定义字体](../js-apis-font.md)。 <br/>默认字体:'HarmonyOS Sans'。 |
563| decoration               | {<br/>type:&nbsp;[TextDecorationType](ts-appendix-enums.md#textdecorationtype),<br/>color?:&nbsp;[ResourceColor](ts-types.md#resourcecolor)<br/>} | 否    | 设置文本装饰线样式及其颜色。<br />默认值:{<br/>type:&nbsp;TextDecorationType.None,<br/>color:Color.Black<br/>}。 |
564| textShadow<sup>11+</sup> | [ShadowOptions](ts-universal-attributes-image-effect.md#shadowoptions对象说明)&nbsp;\|&nbsp;Array&lt;[ShadowOptions](ts-universal-attributes-image-effect.md#shadowoptions对象说明)> | 否    | 设置文字阴影效果。该接口支持以数组形式入参,实现多重文字阴影。<br/>**说明:**<br/>不支持fill字段, 不支持智能取色模式。 |
565
566
567## RichEditorImageSpanOptions
568
569添加图片的偏移位置和图片样式信息。
570
571| 名称                    | 类型                                       | 必填   | 描述                         |
572| --------------------- | ---------------------------------------- | ---- | -------------------------- |
573| offset                | number                                   | 否    | 添加图片的位置。省略时,添加到所有文本字符串的最后。<br/>当值小于0时,放在字符串最前面;当值大于字符串长度时,放在字符串最后面。 |
574| imageStyle            | [RichEditorImageSpanStyle](#richeditorimagespanstyle) | 否    | 图片样式信息。省略时,使用系统默认图片信息。     |
575| gesture<sup>11+</sup> | [RichEditorGesture](#richeditorgesture11) | 否    | 行为触发回调。省略时,仅使用系统默认行为。      |
576
577## RichEditorImageSpanStyle
578
579图片样式。
580
581| 名称                        | 类型                                       | 必填   | 描述                                       |
582| ------------------------- | ---------------------------------------- | ---- | ---------------------------------------- |
583| size                      | [[Dimension](ts-types.md#dimension10), [Dimension](ts-types.md#dimension10)] | 否    | 图片宽度和高度。                                 |
584| verticalAlign             | [ImageSpanAlignment](ts-basic-components-imagespan.md#imagespanalignment) | 否    | 图片垂直对齐方式。<br/>默认值:ImageSpanAlignment.BASELINE |
585| objectFit                 | [ImageFit](ts-appendix-enums.md#imagefit) | 否    | 图片缩放类型。<br/> 默认值:ImageFit.Cover。         |
586| layoutStyle<sup>11+</sup> | [RichEditorLayoutStyle](#richeditorlayoutstyle11) | 否    | 图片布局风格。<br/>                             |
587
588## RichEditorLayoutStyle<sup>11+</sup>
589|名称	|类型	|必填|	描述|
590| -------------  | -----------------------            | ---- | ------------------------------------------------------------ |
591|margin	         |  [Dimension](ts-types.md#dimension10) \| [Margin](ts-types.md#margin)	                       |  否  |	外边距类型,用于描述组件不同方向的外边距。<br/>参数为Dimension类型时,四个方向外边距同时生效。|
592|borderRadius	   |  [Dimension](ts-types.md#dimension10) \| [BorderRadiuses](ts-types.md#borderradiuses9)  |  否  |	圆角类型,用于描述组件边框圆角半径。|
593
594## RichEditorSymbolSpanOptions<sup>11+</sup>
595
596添加文本的偏移位置和文本样式信息。
597
598| 名称     | 类型                                       | 必填   | 描述                         |
599| ------ | ---------------------------------------- | ---- | -------------------------- |
600| offset | number                                   | 否    | 添加组件的位置。省略时,添加到所有文本字符串的最后。<br/>当值小于0时,放在字符串最前面;当值大于字符串长度时,放在字符串最后面。 |
601| style  | [RichEditorSymbolSpanStyle](#richeditorsymbolspanstyle11) | 否    | 组件样式信息。省略时,使用系统默认文本信息。     |
602
603## RichEditorSymbolSpanStyle<sup>11+</sup>
604
605组件SymbolSpan样式信息。
606
607| 名称 | 类型 | 必填 | 描述                               |
608| ------ | -------- | ---- | -------------------------------------- |
609| fontColor | Array\<[ResourceColor](ts-types.md#resourcecolor)\> | 否 | 设置SymbolGlyph组件颜色。<br/> 默认值:不同渲染策略下默认值不同。 |
610| fontSize | number \| string \| [Resource](ts-types.md#resource) | 否 | 设置SymbolGlyph组件大小。<br/>默认值:系统默认值。 |
611| fontWeight | [FontWeight](ts-appendix-enums.md#fontweight) \| number \| string | 否 | 设置SymbolGlyph组件粗细。<br/>number类型取值[100,900],取值间隔为100,默认为400,取值越大,字体越粗。<br/>string类型仅支持number类型取值的字符串形式,例如“400”,以及“bold”、“bolder”、“lighter”、“regular” 、“medium”分别对应FontWeight中相应的枚举值。<br/>默认值:FontWeight.Normal。 |
612| renderingStrategy | [SymbolRenderingStrategy](ts-appendix-enums.md#symbolrenderingstrategy11)	| 否 | 设置SymbolGlyph组件渲染策略。<br/>默认值:SymbolRenderingStrategy.SINGLE。<br/>**说明:**<br/>$r('sys.symbol.ohos_*')中引用的资源仅ohos_trash_circle、ohos_folder_badge_plus、ohos_lungs支持分层与多色模式。 |
613| effectStrategy | [SymbolEffectStrategy](ts-appendix-enums.md#symboleffectstrategy11)	| 否 | 设置SymbolGlyph组件动效策略。<br/>默认值:SymbolEffectStrategy.NONE。<br/>**说明:**<br/>$r('sys.symbol.ohos_*')中引用的资源仅ohos_wifi支持层级动效模式。 |
614
615## RichEditorBuilderSpanOptions<sup>11+</sup>
616
617添加图片的偏移位置和图片样式信息。
618
619| 名称     | 类型     | 必填   | 描述                                    |
620| ------ | ------ | ---- | ------------------------------------- |
621| offset | number | 否    | 添加builder的位置。省略或者为异常值时,添加到所有文本字符串的最后。 |
622
623## RichEditorRange
624
625范围信息。
626
627| 名称    | 类型     | 必填   | 描述                     |
628| ----- | ------ | ---- | ---------------------- |
629| start | number | 否    | 起始位置,省略或者设置负值时表示从0开始。  |
630| end   | number | 否    | 结束位置,省略或者超出文本范围时表示无穷大。 |
631
632## SelectionMenuOptions<sup>11+</sup>
633
634范围信息。
635
636| 名称          | 类型         | 必填   | 描述            |
637| ----------- | ---------- | ---- | ------------- |
638| onAppear    | () => void | 否    | 自定义选择菜单弹出时回调。 |
639| onDisappear | () => void | 否    | 自定义选择菜单关闭时回调。 |
640
641## PasteEvent<sup>11+</sup>
642
643定义用户粘贴事件。
644
645| 名称             | 类型          | 必填   | 描述                            |
646| -------------- | ----------- | ---- | ----------------------------- |
647| preventDefault | () => void | 否    | 阻止系统默认粘贴事件。 |
648
649## RichEditorGesture<sup>11+</sup>
650
651用户行为回调。
652
653### onClick<sup>11+</sup>
654
655onClick(callback: (event?: ClickEvent) => void)
656
657点击完成时回调事件。<br/>
658双击时,第一次点击触发回调事件。
659
660**参数:**
661
662| 参数名   | 参数类型                                     | 必填   | 描述      |
663| ----- | ---------------------------------------- | ---- | ------- |
664| event | [ClickEvent](ts-universal-events-click.md#clickevent对象说明) | 否    | 用户点击事件。 |
665
666### onLongPress<sup>11+</sup>
667
668onLongPress(callback: (event?: GestureEvent) => void )
669
670长按完成时回调事件。
671
672**参数:**
673
674| 参数名   | 参数类型                                     | 必填   | 描述      |
675| ----- | ---------------------------------------- | ---- | ------- |
676| event | [GestureEvent](ts-gesture-settings.md#gestureevent对象说明) | 否    | 用户长按事件。 |
677
678## 示例
679
680### 示例1
681
682```ts
683// xxx.ets
684@Entry
685@Component
686struct Index {
687  controller: RichEditorController = new RichEditorController();
688  options: RichEditorOptions = { controller: this.controller };
689  private start: number = -1;
690  private end: number = -1;
691  @State message: string = "[-1, -1]"
692  @State content: string = ""
693
694  build() {
695    Column() {
696      Column() {
697        Text("selection range:").width("100%")
698        Text() {
699          Span(this.message)
700        }.width("100%")
701        Text("selection content:").width("100%")
702        Text() {
703          Span(this.content)
704        }.width("100%")
705      }
706      .borderWidth(1)
707      .borderColor(Color.Red)
708      .width("100%")
709      .height("20%")
710
711      Row() {
712        Button("更新样式:加粗").onClick(() => {
713          this.controller.updateSpanStyle({
714            start: this.start,
715            end: this.end,
716            textStyle:
717            {
718              fontWeight: FontWeight.Bolder
719            }
720          })
721        })
722        Button("获取选择内容").onClick(() => {
723          this.content = "";
724          this.controller.getSpans({
725            start: this.start,
726            end: this.end
727          }).forEach(item => {
728            if(typeof(item as RichEditorImageSpanResult)['imageStyle'] != 'undefined'){
729              this.content += (item as RichEditorImageSpanResult).valueResourceStr;
730              this.content += "\n"
731            } else {
732              if(typeof(item as RichEditorTextSpanResult)['symbolSpanStyle'] != 'undefined') {
733                this.content += (item as RichEditorTextSpanResult).symbolSpanStyle?.fontSize;
734                this.content += "\n"
735              }else {
736                this.content += (item as RichEditorTextSpanResult).value;
737                this.content += "\n"
738              }
739            }
740          })
741        })
742        Button("删除选择内容").onClick(() => {
743          this.controller.deleteSpans({
744            start: this.start,
745            end: this.end
746          })
747          this.start = -1;
748          this.end = -1;
749          this.message = "[" + this.start + ", " + this.end + "]"
750        })
751      }
752      .borderWidth(1)
753      .borderColor(Color.Red)
754      .width("100%")
755      .height("10%")
756
757      Column() {
758        RichEditor(this.options)
759          .onReady(() => {
760            this.controller.addTextSpan("012345",
761              {
762                style:
763                {
764                  fontColor: Color.Orange,
765                  fontSize: 30
766                }
767              })
768            this.controller.addSymbolSpan($r("sys.symbol.ohos_trash"),
769              {
770                style:
771                {
772                  fontSize: 30
773                }
774              })
775            this.controller.addImageSpan($r("app.media.icon"),
776              {
777                imageStyle:
778                {
779                  size: ["57px", "57px"]
780                }
781              })
782            this.controller.addTextSpan("56789",
783              {
784                style:
785                {
786                  fontColor: Color.Black,
787                  fontSize: 30
788                }
789              })
790          })
791          .onSelect((value: RichEditorSelection) => {
792            this.start = value.selection[0];
793            this.end = value.selection[1];
794            this.message = "[" + this.start + ", " + this.end + "]"
795          })
796          .aboutToIMEInput((value: RichEditorInsertValue) => {
797            console.log("---------------------- aboutToIMEInput ----------------------")
798            console.log("insertOffset:" + value.insertOffset)
799            console.log("insertValue:" + value.insertValue)
800            return true;
801          })
802          .onIMEInputComplete((value: RichEditorTextSpanResult) => {
803            console.log("---------------------- onIMEInputComplete ---------------------")
804            console.log("spanIndex:" + value.spanPosition.spanIndex)
805            console.log("spanRange:[" + value.spanPosition.spanRange[0] + "," + value.spanPosition.spanRange[1] + "]")
806            console.log("offsetInSpan:[" + value.offsetInSpan[0] + "," + value.offsetInSpan[1] + "]")
807            console.log("value:" + value.value)
808          })
809          .aboutToDelete((value: RichEditorDeleteValue) => {
810            console.log("---------------------- aboutToDelete --------------------------")
811            console.log("offset:" + value.offset)
812            console.log("direction:" + value.direction)
813            console.log("length:" + value.length)
814            value.richEditorDeleteSpans.forEach(item => {
815              console.log("---------------------- item --------------------------")
816              console.log("spanIndex:" + item.spanPosition.spanIndex)
817              console.log("spanRange:[" + item.spanPosition.spanRange[0] + "," + item.spanPosition.spanRange[1] + "]")
818              console.log("offsetInSpan:[" + item.offsetInSpan[0] + "," + item.offsetInSpan[1] + "]")
819              if (typeof(item as RichEditorImageSpanResult)['imageStyle'] != 'undefined') {
820                console.log("image:" + (item as RichEditorImageSpanResult).valueResourceStr)
821              } else {
822                console.log("text:" + (item as RichEditorTextSpanResult).value)
823              }
824            })
825            return true;
826          })
827          .onDeleteComplete(() => {
828            console.log("---------------------- onDeleteComplete ------------------------")
829          })
830          .placeholder("input...", {
831            fontColor: Color.Gray,
832            font: {
833              size: 16,
834              weight: FontWeight.Normal,
835              family: "HarmonyOS Sans",
836              style: FontStyle.Normal
837            }
838          })
839          .borderWidth(1)
840          .borderColor(Color.Green)
841          .width("100%")
842          .height("30%")
843      }
844      .borderWidth(1)
845      .borderColor(Color.Red)
846      .width("100%")
847      .height("70%")
848    }
849  }
850}
851```
852![richeditor](figures/richeditor.gif)
853
854### 示例2
855
856```ts
857// xxx.ets
858@Entry
859@Component
860struct RichEditorExample {
861  controller: RichEditorController = new RichEditorController()
862
863  // 自定义键盘组件
864  @Builder CustomKeyboardBuilder() {
865    Column() {
866      Grid() {
867        ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9, '*', 0, '#'], (item: number | string) => {
868          GridItem() {
869            Button(item + "")
870              .width(110).onClick(() => {
871              this.controller.addTextSpan(item + '', {
872                offset: this.controller.getCaretOffset(),
873                style:
874                {
875                  fontColor: Color.Orange,
876                  fontSize: 30
877                }
878              })
879              this.controller.setCaretOffset(this.controller.getCaretOffset() + item.toString().length)
880            })
881          }
882        })
883      }.maxCount(3).columnsGap(10).rowsGap(10).padding(5)
884    }.backgroundColor(Color.Gray)
885  }
886
887  build() {
888    Column() {
889      RichEditor({ controller: this.controller })
890        // 绑定自定义键盘
891        .customKeyboard(this.CustomKeyboardBuilder()).margin(10).border({ width: 1 })
892        .height(200)
893        .borderWidth(1)
894        .borderColor(Color.Red)
895        .width("100%")
896    }
897  }
898}
899```
900
901![customKeyboard](figures/richEditorCustomKeyboard.gif)
902
903### 示例3
904
905```ts
906// xxx.ets
907import pasteboard from '@ohos.pasteboard'
908import { BusinessError } from '@ohos.base';
909
910export interface SelectionMenuTheme {
911  imageSize: number;
912  buttonSize: number;
913  menuSpacing: number;
914  editorOptionMargin: number;
915  expandedOptionPadding: number;
916  defaultMenuWidth: number;
917  imageFillColor: Resource;
918  backGroundColor: Resource;
919  iconBorderRadius: Resource;
920  containerBorderRadius: Resource;
921  cutIcon: Resource;
922  copyIcon: Resource;
923  pasteIcon: Resource;
924  selectAllIcon: Resource;
925  shareIcon: Resource;
926  translateIcon: Resource;
927  searchIcon: Resource;
928  arrowDownIcon: Resource;
929  iconPanelShadowStyle: ShadowStyle;
930  iconFocusBorderColor: Resource;
931}
932
933export const defaultTheme: SelectionMenuTheme = {
934  imageSize: 24,
935  buttonSize: 48,
936  menuSpacing: 8,
937  editorOptionMargin: 1,
938  expandedOptionPadding: 3,
939  defaultMenuWidth: 256,
940  imageFillColor: $r('sys.color.ohos_id_color_primary'),
941  backGroundColor: $r('sys.color.ohos_id_color_dialog_bg'),
942  iconBorderRadius: $r('sys.float.ohos_id_corner_radius_default_m'),
943  containerBorderRadius: $r('sys.float.ohos_id_corner_radius_card'),
944  cutIcon: $r("sys.media.ohos_ic_public_cut"),
945  copyIcon: $r("sys.media.ohos_ic_public_copy"),
946  pasteIcon: $r("sys.media.ohos_ic_public_paste"),
947  selectAllIcon: $r("sys.media.ohos_ic_public_select_all"),
948  shareIcon: $r("sys.media.ohos_ic_public_share"),
949  translateIcon: $r("sys.media.ohos_ic_public_translate_c2e"),
950  searchIcon: $r("sys.media.ohos_ic_public_search_filled"),
951  arrowDownIcon: $r("sys.media.ohos_ic_public_arrow_down"),
952  iconPanelShadowStyle: ShadowStyle.OUTER_DEFAULT_MD,
953  iconFocusBorderColor: $r('sys.color.ohos_id_color_focused_outline'),
954}
955
956@Entry
957@Component
958struct SelectionMenu {
959  @State message: string = 'Hello World'
960  @State textSize: number = 40
961  @State sliderShow: boolean = false
962  @State start: number = -1
963  @State end: number = -1
964  @State colorTransparent: Color = Color.Transparent
965  controller: RichEditorController = new RichEditorController();
966  options: RichEditorOptions = { controller: this.controller }
967  private iconArr: Array<Resource> =
968    [$r('app.media.icon'), $r("app.media.icon"), $r('app.media.icon'),
969    $r("app.media.icon"), $r('app.media.icon')]
970  @State iconBgColor: ResourceColor[] = new Array(this.iconArr.length).fill(this.colorTransparent)
971  @State pasteEnable: boolean = false
972  @State visibilityValue: Visibility = Visibility.Visible
973  @State textStyle: RichEditorTextStyle = {}
974  private fontWeightTable: string[] = ["100", "200", "300", "400", "500", "600", "700", "800", "900", "bold", "normal", "bolder", "lighter", "medium", "regular"]
975  private theme: SelectionMenuTheme = defaultTheme;
976
977  aboutToAppear() {
978    if (this.controller) {
979      let richEditorSelection = this.controller.getSelection()
980      if (richEditorSelection) {
981        let start = richEditorSelection.selection[0]
982        let end = richEditorSelection.selection[1]
983        if (start === 0 && this.controller.getSpans({ start: end + 1, end: end + 1 }).length === 0) {
984          this.visibilityValue = Visibility.None
985        } else {
986          this.visibilityValue = Visibility.Visible
987        }
988      }
989    }
990    let sysBoard = pasteboard.getSystemPasteboard()
991    if (sysBoard && sysBoard.hasDataSync()) {
992      this.pasteEnable = true
993    } else {
994      this.pasteEnable = false
995    }
996  }
997
998  build() {
999    Column() {
1000      Column() {
1001        RichEditor(this.options)
1002          .onReady(() => {
1003            this.controller.addTextSpan(this.message, { style: { fontColor: Color.Orange, fontSize: 30 } })
1004          })
1005          .onSelect((value: RichEditorSelection) => {
1006            if (value.selection[0] == -1 && value.selection[1] == -1) {
1007              return
1008            }
1009            this.start = value.selection[0]
1010            this.end = value.selection[1]
1011          })
1012          .bindSelectionMenu(RichEditorSpanType.TEXT, this.panel, ResponseType.LongPress, { onDisappear: () => {
1013            this.sliderShow = false
1014          }})
1015          .bindSelectionMenu(RichEditorSpanType.TEXT, this.panel, ResponseType.RightClick, { onDisappear: () => {
1016            this.sliderShow = false
1017          }})
1018          .borderWidth(1)
1019          .borderColor(Color.Red)
1020          .width(200)
1021          .height(200)
1022      }.width('100%').backgroundColor(Color.White)
1023    }.height('100%')
1024  }
1025
1026  PushDataToPasteboard(richEditorSelection: RichEditorSelection) {
1027    let sysBoard = pasteboard.getSystemPasteboard()
1028    let pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, '')
1029    if (richEditorSelection.spans && richEditorSelection.spans.length > 0) {
1030      let count = richEditorSelection.spans.length
1031      for (let i = count - 1; i >= 0; i--) {
1032        let item = richEditorSelection.spans[i]
1033        if ((item as RichEditorTextSpanResult)?.textStyle) {
1034          let span = item as RichEditorTextSpanResult
1035          let style = span.textStyle
1036          let data = pasteboard.createRecord(pasteboard.MIMETYPE_TEXT_PLAIN, span.value.substring(span.offsetInSpan[0], span.offsetInSpan[1]))
1037          let prop = pasteData.getProperty()
1038          let temp: Record<string, Object> = {
1039            'color': style.fontColor,
1040            'size': style.fontSize,
1041            'style': style.fontStyle,
1042            'weight': this.fontWeightTable[style.fontWeight],
1043            'fontFamily': style.fontFamily,
1044            'decorationType': style.decoration.type,
1045            'decorationColor': style.decoration.color
1046          }
1047          prop.additions[i] = temp;
1048          pasteData.addRecord(data)
1049          pasteData.setProperty(prop)
1050        }
1051      }
1052    }
1053    sysBoard.clearData()
1054    sysBoard.setData(pasteData).then(() => {
1055      console.info('SelectionMenu copy option, Succeeded in setting PasteData.');
1056      this.pasteEnable = true;
1057    }).catch((err: BusinessError) => {
1058      console.error('SelectionMenu copy option, Failed to set PasteData. Cause:' + err.message);
1059    })
1060  }
1061
1062  PopDataFromPasteboard(richEditorSelection: RichEditorSelection) {
1063    let start = richEditorSelection.selection[0]
1064    let end = richEditorSelection.selection[1]
1065    if (start == end && this.controller) {
1066      start = this.controller.getCaretOffset()
1067      end = this.controller.getCaretOffset()
1068    }
1069    let moveOffset = 0
1070    let sysBoard = pasteboard.getSystemPasteboard()
1071    sysBoard.getData((err, data) => {
1072      if (err) {
1073        return
1074      }
1075      let count = data.getRecordCount()
1076      for (let i = 0; i < count; i++) {
1077        const element = data.getRecord(i);
1078        let tex: RichEditorTextStyle = {
1079          fontSize: 16,
1080          fontColor: Color.Black,
1081          fontWeight: FontWeight.Normal,
1082          fontFamily: "HarmonyOS Sans",
1083          fontStyle: FontStyle.Normal,
1084          decoration: { type: TextDecorationType.None, color: "#FF000000" }
1085        }
1086        if (data.getProperty() && data.getProperty().additions[i]) {
1087          const tmp = data.getProperty().additions[i] as Record<string, Object | undefined>;
1088          if (tmp.color) {
1089            tex.fontColor = tmp.color as ResourceColor;
1090          }
1091          if (tmp.size) {
1092            tex.fontSize = tmp.size as Length | number;
1093          }
1094          if (tmp.style) {
1095            tex.fontStyle = tmp.style as FontStyle;
1096          }
1097          if (tmp.weight) {
1098            tex.fontWeight = tmp.weight as number | FontWeight | string;
1099          }
1100          if (tmp.fontFamily) {
1101            tex.fontFamily = tmp.fontFamily as ResourceStr;
1102          }
1103          if (tmp.decorationType && tex.decoration) {
1104            tex.decoration.type = tmp.decorationType as TextDecorationType;
1105          }
1106          if (tmp.decorationColor && tex.decoration) {
1107            tex.decoration.color = tmp.decorationColor as ResourceColor;
1108          }
1109          if (tex.decoration) {
1110            tex.decoration = { type: tex.decoration.type, color: tex.decoration.color }
1111          }
1112        }
1113        if (element && element.plainText && element.mimeType === pasteboard.MIMETYPE_TEXT_PLAIN && this.controller) {
1114          this.controller.addTextSpan(element.plainText,
1115            {
1116              style: tex,
1117              offset: start + moveOffset
1118            }
1119          )
1120          moveOffset += element.plainText.length
1121        }
1122      }
1123      if (this.controller) {
1124        this.controller.setCaretOffset(start + moveOffset)
1125        this.controller.closeSelectionMenu()
1126      }
1127      if (start != end && this.controller) {
1128        this.controller.deleteSpans({ start: start + moveOffset, end: end + moveOffset })
1129      }
1130    })
1131  }
1132
1133  @Builder
1134  panel() {
1135    Column() {
1136      this.iconPanel()
1137      if (!this.sliderShow) {
1138        this.SystemMenu()
1139      } else {
1140        this.sliderPanel()
1141      }
1142    }.width(256)
1143  }
1144
1145  @Builder iconPanel() {
1146    Column() {
1147      Row({ space: 2 }) {
1148        ForEach(this.iconArr, (item:Resource, index ?: number) => {
1149          Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
1150            Image(item).fillColor(this.theme.imageFillColor).width(24).height(24).focusable(true).draggable(false)
1151          }
1152          .borderRadius(this.theme.iconBorderRadius)
1153          .width(this.theme.buttonSize)
1154          .height(this.theme.buttonSize)
1155          .onClick(() => {
1156            if (index as number == 0) {
1157              this.sliderShow = false
1158              if (this.controller) {
1159                let selection = this.controller.getSelection();
1160                let spans = selection.spans
1161                spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
1162                  if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
1163                    let span = item as RichEditorTextSpanResult
1164                    this.textStyle = span.textStyle
1165                    let start = span.offsetInSpan[0]
1166                    let end = span.offsetInSpan[1]
1167                    let offset = span.spanPosition.spanRange[0]
1168                    if (this.textStyle.fontWeight != 11) {
1169                      this.textStyle.fontWeight = FontWeight.Bolder
1170                    } else {
1171                      this.textStyle.fontWeight = FontWeight.Normal
1172                    }
1173                    this.controller.updateSpanStyle({
1174                      start: offset + start,
1175                      end: offset + end,
1176                      textStyle: this.textStyle
1177                    })
1178                  }
1179                })
1180              }
1181            } else if (index as number == 1) {
1182              this.sliderShow = false
1183              if (this.controller) {
1184                let selection = this.controller.getSelection();
1185                let spans = selection.spans
1186                spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
1187                  if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
1188                    let span = item as RichEditorTextSpanResult
1189                    this.textStyle = span.textStyle
1190                    let start = span.offsetInSpan[0]
1191                    let end = span.offsetInSpan[1]
1192                    let offset = span.spanPosition.spanRange[0]
1193                    if (this.textStyle.fontStyle == FontStyle.Italic) {
1194                      this.textStyle.fontStyle = FontStyle.Normal
1195                    } else {
1196                      this.textStyle.fontStyle = FontStyle.Italic
1197                    }
1198                    this.controller.updateSpanStyle({
1199                      start: offset + start,
1200                      end: offset + end,
1201                      textStyle: this.textStyle
1202                    })
1203                  }
1204                })
1205              }
1206            } else if (index as number == 2) {
1207              this.sliderShow = false
1208              if (this.controller) {
1209                let selection = this.controller.getSelection();
1210                let spans = selection.spans
1211                spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
1212                  if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
1213                    let span = item as RichEditorTextSpanResult
1214                    this.textStyle = span.textStyle
1215                    let start = span.offsetInSpan[0]
1216                    let end = span.offsetInSpan[1]
1217                    let offset = span.spanPosition.spanRange[0]
1218                    if (this.textStyle.decoration) {
1219                      if (this.textStyle.decoration.type == TextDecorationType.Underline) {
1220                        this.textStyle.decoration.type = TextDecorationType.None
1221                      } else {
1222                        this.textStyle.decoration.type = TextDecorationType.Underline
1223                      }
1224                    } else {
1225                      this.textStyle.decoration = { type: TextDecorationType.Underline, color: Color.Black }
1226                    }
1227                    this.controller.updateSpanStyle({
1228                      start: offset + start,
1229                      end: offset + end,
1230                      textStyle: this.textStyle
1231                    })
1232                  }
1233                })
1234              }
1235            } else if (index as number == 3) {
1236              this.sliderShow = !this.sliderShow
1237            } else if (index as number == 4) {
1238              this.sliderShow = false
1239              if (this.controller) {
1240                let selection = this.controller.getSelection();
1241                let spans = selection.spans
1242                spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
1243                  if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
1244                    let span = item as RichEditorTextSpanResult
1245                    this.textStyle = span.textStyle
1246                    let start = span.offsetInSpan[0]
1247                    let end = span.offsetInSpan[1]
1248                    let offset = span.spanPosition.spanRange[0]
1249                    if (this.textStyle.fontColor == Color.Orange || this.textStyle.fontColor == '#FFFFA500') {
1250                      this.textStyle.fontColor = Color.Black
1251                    } else {
1252                      this.textStyle.fontColor = Color.Orange
1253                    }
1254                    this.controller.updateSpanStyle({
1255                      start: offset + start,
1256                      end: offset + end,
1257                      textStyle: this.textStyle
1258                    })
1259                  }
1260                })
1261              }
1262            }
1263          })
1264          .onTouch((event?: TouchEvent | undefined) => {
1265            if(event != undefined){
1266              if (event.type === TouchType.Down) {
1267                this.iconBgColor[index as number] = $r('sys.color.ohos_id_color_click_effect')
1268              }
1269              if (event.type === TouchType.Up) {
1270                this.iconBgColor[index as number] = this.colorTransparent
1271              }
1272            }
1273          })
1274          .onHover((isHover?: boolean, event?: HoverEvent) => {
1275            this.iconBgColor.forEach((icon:ResourceColor, index1) => {
1276              this.iconBgColor[index1] = this.colorTransparent
1277            })
1278            if(isHover != undefined) {
1279              this.iconBgColor[index as number] = $r('sys.color.ohos_id_color_hover')
1280            }
1281          })
1282          .backgroundColor(this.iconBgColor[index as number])
1283        })
1284      }
1285    }
1286    .clip(true)
1287    .width(this.theme.defaultMenuWidth)
1288    .padding(this.theme.expandedOptionPadding)
1289    .borderRadius(this.theme.containerBorderRadius)
1290    .margin({ bottom: this.theme.menuSpacing })
1291    .backgroundColor(this.theme.backGroundColor)
1292    .shadow(this.theme.iconPanelShadowStyle)
1293  }
1294
1295  @Builder
1296  SystemMenu() {
1297    Column() {
1298      Menu() {
1299        if (this.controller) {
1300          MenuItemGroup() {
1301            MenuItem({ startIcon: this.theme.cutIcon, content: "剪切", labelInfo: "Ctrl+X" })
1302              .onClick(() => {
1303                if (!this.controller) {
1304                  return
1305                }
1306                let richEditorSelection = this.controller.getSelection()
1307                this.PushDataToPasteboard(richEditorSelection);
1308                this.controller.deleteSpans({
1309                  start: richEditorSelection.selection[0],
1310                  end: richEditorSelection.selection[1]
1311                })
1312              })
1313            MenuItem({ startIcon: this.theme.copyIcon, content: "复制", labelInfo: "Ctrl+C" })
1314              .onClick(() => {
1315                if (!this.controller) {
1316                  return
1317                }
1318                let richEditorSelection = this.controller.getSelection()
1319                this.PushDataToPasteboard(richEditorSelection);
1320                this.controller.closeSelectionMenu()
1321              })
1322            MenuItem({ startIcon: this.theme.pasteIcon, content: "粘贴", labelInfo: "Ctrl+V" })
1323              .enabled(this.pasteEnable)
1324              .onClick(() => {
1325                if (!this.controller) {
1326                  return
1327                }
1328                let richEditorSelection = this.controller.getSelection()
1329                this.PopDataFromPasteboard(richEditorSelection)
1330              })
1331            MenuItem({ startIcon: this.theme.selectAllIcon, content: "全选", labelInfo: "Ctrl+A" })
1332              .visibility(this.visibilityValue)
1333              .onClick(() => {
1334                if (!this.controller) {
1335                  return
1336                }
1337                this.controller.setSelection(-1, -1)
1338                this.visibilityValue = Visibility.None
1339              })
1340            MenuItem({ startIcon: this.theme.shareIcon, content: "分享", labelInfo: "" })
1341              .enabled(false)
1342            MenuItem({ startIcon: this.theme.translateIcon, content: "翻译", labelInfo: "" })
1343              .enabled(false)
1344            MenuItem({ startIcon: this.theme.searchIcon, content: "搜索", labelInfo: "" })
1345              .enabled(false)
1346          }
1347        }
1348      }
1349      .onVisibleAreaChange([0.0, 1.0], () => {
1350        if (!this.controller) {
1351          return
1352        }
1353        let richEditorSelection = this.controller.getSelection()
1354        let start = richEditorSelection.selection[0]
1355        let end = richEditorSelection.selection[1]
1356        if (start === 0 && this.controller.getSpans({ start: end + 1, end: end + 1 }).length === 0) {
1357          this.visibilityValue = Visibility.None
1358        } else {
1359          this.visibilityValue = Visibility.Visible
1360        }
1361      })
1362      .radius(this.theme.containerBorderRadius)
1363      .clip(true)
1364      .backgroundColor(Color.White)
1365      .width(this.theme.defaultMenuWidth)
1366    }
1367    .width(this.theme.defaultMenuWidth)
1368  }
1369
1370  @Builder sliderPanel() {
1371    Column() {
1372      Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
1373        Text('A').fontSize(15)
1374        Slider({ value: this.textSize, step: 10, style: SliderStyle.InSet })
1375          .width(210)
1376          .onChange((value: number, mode: SliderChangeMode) => {
1377            if (this.controller) {
1378              let selection = this.controller.getSelection();
1379              if (mode == SliderChangeMode.End) {
1380                if (this.textSize == undefined) {
1381                  this.textSize = 0
1382                }
1383                let spans = selection.spans
1384                spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
1385                  if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
1386                    this.textSize = Math.max(this.textSize, (item as RichEditorTextSpanResult).textStyle.fontSize)
1387                  }
1388                })
1389              }
1390              if (mode == SliderChangeMode.Moving || mode == SliderChangeMode.Click) {
1391                this.start = selection.selection[0]
1392                this.end = selection.selection[1]
1393                this.textSize = value
1394                this.controller.updateSpanStyle({
1395                  start: this.start,
1396                  end: this.end,
1397                  textStyle: { fontSize: this.textSize }
1398                })
1399              }
1400            }
1401          })
1402        Text('A').fontSize(20).fontWeight(FontWeight.Medium)
1403      }.borderRadius(this.theme.containerBorderRadius)
1404    }
1405    .shadow(ShadowStyle.OUTER_DEFAULT_MD)
1406    .backgroundColor(Color.White)
1407    .borderRadius(this.theme.containerBorderRadius)
1408    .padding(15)
1409    .height(48)
1410  }
1411}
1412```
1413> **说明:**
1414>
1415> 系统暂未预置加粗、斜体等图标,示例代码使用系统默认图标,开发者使用时需自行替换iconArr中的资源。
1416
1417![selectionMenu](figures/richEditorSelectionMenu.png)
1418
1419### 示例4
1420
1421```ts
1422// xxx.ets
1423@Entry
1424@Component
1425struct Index {
1426  controller: RichEditorController = new RichEditorController();
1427  options: RichEditorOptions = { controller: this.controller };
1428  private start: number = -1;
1429  private end: number = -1;
1430  @State message: string = "[-1, -1]"
1431  @State content: string = ""
1432  @State paddingVal: number = 5
1433  @State borderRad: number = 4
1434
1435  build() {
1436    Column() {
1437      Column() {
1438        Text("selection range:").width("100%")
1439        Text() {
1440          Span(this.message)
1441        }.width("100%")
1442        Text("selection content:").width("100%")
1443        Text() {
1444          Span(this.content)
1445        }.width("100%")
1446      }
1447      .borderWidth(1)
1448      .borderColor(Color.Red)
1449      .width("100%")
1450      .height("20%")
1451
1452      Row() {
1453        Button("updateSpanStyle1")
1454          .fontSize(12)
1455          .onClick(() => {
1456            this.controller.updateSpanStyle({
1457              start: this.start,
1458              textStyle:
1459              {
1460                fontWeight: FontWeight.Bolder
1461              },
1462              imageStyle: {
1463                size: ["80px", "80px"],
1464                layoutStyle: {
1465                  borderRadius: undefined,
1466                  margin: undefined
1467                }
1468              }
1469            })
1470          })
1471
1472        Button("updateSpanStyle2")
1473          .fontSize(12)
1474          .onClick(() => {
1475            this.controller.updateSpanStyle({
1476              start: this.start,
1477              textStyle:
1478              {
1479                fontWeight: FontWeight.Bolder
1480              },
1481              imageStyle: {
1482                size: ["70px", "70px"],
1483                layoutStyle: {
1484                  borderRadius: { topLeft: '100px', topRight: '20px', bottomLeft: '100px', bottomRight: '20px' },
1485                  margin: { left: '30px', top: '20px', right: '20px', bottom: '20px' }
1486                }
1487              }
1488            })
1489          })
1490
1491        Button("updateSpanStyle3")
1492          .fontSize(12)
1493          .onClick(() => {
1494            this.controller.updateSpanStyle({
1495              start: this.start,
1496              textStyle:
1497              {
1498                fontWeight: FontWeight.Bolder
1499              },
1500              imageStyle: {
1501                size: ["60px", "60px"],
1502                layoutStyle: {
1503                  borderRadius: '-10px',
1504                  margin: '-10px'
1505                }
1506              }
1507            })
1508          })
1509      }
1510      .borderWidth(1)
1511      .borderColor(Color.Red)
1512      .width("100%")
1513      .height("10%")
1514
1515      Row() {
1516        Button('addImageSpan1')
1517          .fontSize(12)
1518          .onClick(() => {
1519            this.controller.addImageSpan($r('app.media.app_icon'), {
1520              imageStyle: {
1521                size: ["80px", "80px"],
1522                layoutStyle: {
1523                  borderRadius: '50px',
1524                  margin: '40px'
1525                }
1526              }
1527            })
1528          })
1529
1530        Button('addImageSpan2')
1531          .fontSize(12)
1532          .onClick(() => {
1533            this.controller.addImageSpan($r('app.media.app_icon'), {
1534              imageStyle: {
1535                size: ["100px", "100px"],
1536                verticalAlign: ImageSpanAlignment.BOTTOM,
1537                layoutStyle: {
1538                  borderRadius: undefined,
1539                  margin: undefined
1540                }
1541              }
1542            })
1543          })
1544
1545        Button('addImageSpan3')
1546          .fontSize(12)
1547          .onClick(() => {
1548            this.controller.addImageSpan($r('app.media.app_icon'), {
1549              imageStyle: {
1550                size: ["60px", "60px"],
1551                verticalAlign: ImageSpanAlignment.BOTTOM,
1552                layoutStyle: {
1553                  borderRadius: { topLeft: '10px', topRight: '20px', bottomLeft: '30px', bottomRight: '40px' },
1554                  margin: { left: '10px', top: '20px', right: '30px', bottom: '40px' }
1555                }
1556              }
1557            })
1558          })
1559      }
1560      .borderWidth(1)
1561      .borderColor(Color.Red)
1562      .width("100%")
1563      .height("10%")
1564
1565      Column() {
1566        RichEditor(this.options)
1567          .onReady(() => {
1568            this.controller.addTextSpan("0123456789",
1569              {
1570                style:
1571                {
1572                  fontColor: Color.Orange,
1573                  fontSize: 30
1574                }
1575              })
1576
1577            this.controller.addImageSpan($r("app.media.app_icon"),
1578              {
1579                imageStyle:
1580                {
1581                  size: ["60px", "60px"],
1582                  verticalAlign: ImageSpanAlignment.BOTTOM,
1583                  layoutStyle: {
1584                    borderRadius: { topLeft: '10px', topRight: '20px', bottomLeft: '30px', bottomRight: '40px' },
1585                    margin: { left: '10px', top: '20px', right: '30px', bottom: '40px' }
1586                  }
1587                }
1588              })
1589
1590            this.controller.addTextSpan("0123456789",
1591              {
1592                style:
1593                {
1594                  fontColor: Color.Black,
1595                  fontSize: 30
1596                }
1597              })
1598          })
1599          .onSelect((value: RichEditorSelection) => {
1600            this.start = value.selection[0];
1601            this.end = value.selection[1];
1602            this.message = "[" + this.start + ", " + this.end + "]"
1603          })
1604          .aboutToIMEInput((value: RichEditorInsertValue) => {
1605            console.log("---------------------- aboutToIMEInput ----------------------")
1606            console.log("insertOffset:" + value.insertOffset)
1607            console.log("insertValue:" + value.insertValue)
1608            return true;
1609          })
1610          .onIMEInputComplete((value: RichEditorTextSpanResult) => {
1611            console.log("---------------------- onIMEInputComplete ---------------------")
1612            console.log("spanIndex:" + value.spanPosition.spanIndex)
1613            console.log("spanRange:[" + value.spanPosition.spanRange[0] + "," + value.spanPosition.spanRange[1] + "]")
1614            console.log("offsetInSpan:[" + value.offsetInSpan[0] + "," + value.offsetInSpan[1] + "]")
1615            console.log("value:" + value.value)
1616          })
1617          .aboutToDelete((value: RichEditorDeleteValue) => {
1618            console.log("---------------------- aboutToDelete --------------------------")
1619            console.log("offset:" + value.offset)
1620            console.log("direction:" + value.direction)
1621            console.log("length:" + value.length)
1622            value.richEditorDeleteSpans.forEach(item => {
1623              console.log("---------------------- item --------------------------")
1624              console.log("spanIndex:" + item.spanPosition.spanIndex)
1625              console.log("spanRange:[" + item.spanPosition.spanRange[0] + "," + item.spanPosition.spanRange[1] + "]")
1626              console.log("offsetInSpan:[" + item.offsetInSpan[0] + "," + item.offsetInSpan[1] + "]")
1627              if (typeof (item as RichEditorImageSpanResult)['imageStyle'] != 'undefined') {
1628                console.log("image:" + (item as RichEditorImageSpanResult).valueResourceStr)
1629              } else {
1630                console.log("text:" + (item as RichEditorTextSpanResult).value)
1631              }
1632            })
1633            return true;
1634          })
1635          .onDeleteComplete(() => {
1636            console.log("---------------------- onDeleteComplete ------------------------")
1637          })
1638          .borderWidth(1)
1639          .borderColor(Color.Green)
1640          .width("100%")
1641          .height('80.00%')
1642      }
1643      .borderWidth(1)
1644      .borderColor(Color.Red)
1645      .width("100%")
1646      .height("70%")
1647    }
1648  }
1649}
1650```
1651![ImageSpanStyle](figures/richEditorImageSpanStyle.gif)
1652
1653### 示例5
1654
1655```ts
1656// xxx.ets
1657@Entry
1658@Component
1659struct Index {
1660  controller: RichEditorController = new RichEditorController()
1661  options: RichEditorOptions = { controller: this.controller };
1662  @State textFlag: string = "TextFlag";
1663
1664  build() {
1665    Column() {
1666      Column() {
1667        Text(this.textFlag)
1668          .copyOption(CopyOptions.InApp)
1669          .fontSize(50)
1670      }
1671      Divider()
1672      Column() {
1673        RichEditor(this.options)
1674          .onReady(() => {
1675            this.controller.addTextSpan('Area1\n', {
1676              style:
1677              {
1678                fontColor: Color.Orange,
1679                fontSize: 50
1680              },
1681              gesture:
1682              {
1683                onClick: () => {
1684                  this.textFlag = "Area1 is onClick."
1685                },
1686                onLongPress: () => {
1687                  this.textFlag = "Area1 is onLongPress."
1688                }
1689              }
1690            })
1691
1692            this.controller.addTextSpan('Area2\n', {
1693              style:
1694              {
1695                fontColor: Color.Blue,
1696                fontSize: 50
1697              },
1698              gesture:
1699              {
1700                onClick: () => {
1701                  this.textFlag = "Area2 is onClick."
1702                },
1703                onLongPress: () => {
1704                  this.textFlag = "Area2 is onLongPress."
1705                }
1706              }
1707            })
1708
1709            this.controller.addImageSpan($r("app.media.icon"),
1710              {
1711                imageStyle:
1712                {
1713                  size: ["100px", "100px"],
1714                  layoutStyle: {
1715                    margin: 5,
1716                    borderRadius: 15
1717                  }
1718                },
1719                gesture:
1720                {
1721                  onClick: () => {
1722                    this.textFlag = "ImageSpan is onClick."
1723                  },
1724                  onLongPress: () => {
1725                    this.textFlag = "ImageSpan is onLongPress."
1726                  }
1727                }
1728              })
1729          })
1730      }
1731      .borderWidth(1)
1732      .borderColor(Color.Red)
1733      .width("100%")
1734      .height("70%")
1735    }
1736  }
1737}
1738```
1739![OnClickAndLongPress](figures/richEditorOnClickAndLongPress.gif)
1740
1741### 示例6
1742
1743```ts
1744// xxx.ets
1745@Entry
1746@Component
1747struct Index {
1748  controller: RichEditorController = new RichEditorController();
1749  private spanParagraphs: RichEditorParagraphResult[] = [];
1750
1751  build() {
1752    Column() {
1753      RichEditor({ controller: this.controller })
1754        .onReady(() => {
1755          this.controller.addTextSpan("0123456789\n", {
1756            style: {
1757              fontColor: Color.Pink,
1758              fontSize: "32",
1759            },
1760            paragraphStyle: {
1761              textAlign: TextAlign.Start,
1762              leadingMargin: 16
1763            }
1764          })
1765          this.controller.addTextSpan("0123456789")
1766        })
1767        .width("80%")
1768        .height("30%")
1769        .border({ width: 1, radius: 5 })
1770        .draggable(false)
1771
1772      Column({ space: 5 }) {
1773        Button("段落左对齐").onClick(() => {
1774          this.controller.updateParagraphStyle({ start: -1, end: -1,
1775            style: {
1776              textAlign: TextAlign.Start,
1777            }
1778          })
1779        })
1780
1781        Button("段落右对齐").onClick(() => {
1782          this.controller.updateParagraphStyle({ start: -1, end: -1,
1783            style: {
1784              textAlign: TextAlign.End,
1785            }
1786          })
1787        })
1788
1789        Button("段落居中").onClick(() => {
1790          this.controller.updateParagraphStyle({ start: -1, end: -1,
1791            style: {
1792              textAlign: TextAlign.Center,
1793            }
1794          })
1795        })
1796        Divider()
1797        Button("getParagraphs").onClick(() => {
1798          this.spanParagraphs = this.controller.getParagraphs({ start: -1, end: -1 })
1799          console.log("RichEditor getParagraphs:" + JSON.stringify(this.spanParagraphs))
1800        })
1801
1802        Button("UpdateSpanStyle1").onClick(() => {
1803          this.controller.updateSpanStyle({ start: -1, end: -1,
1804            textStyle: {
1805              fontColor: Color.Brown,
1806              fontSize: 20
1807            }
1808          })
1809        })
1810
1811        Button("UpdateSpanStyle2").onClick(() => {
1812          this.controller.updateSpanStyle({ start: -1, end: -1,
1813            textStyle: {
1814              fontColor: Color.Green,
1815              fontSize: 30
1816            }
1817          })
1818        })
1819      }
1820    }
1821  }
1822}
1823```
1824![TextAlignAndGetParagraphInfo](figures/richEditorTextAlignAndGetParagraphInfo.gif)
1825
1826### 示例7
1827
1828```ts
1829// xxx.ets
1830import font from '@ohos.font'
1831const canvasWidth = 1000
1832const canvasHeight = 100
1833const Indentation = 40
1834class LeadingMarginCreator {
1835  private settings: RenderingContextSettings = new RenderingContextSettings(true)
1836  private offscreenCanvas: OffscreenCanvas = new OffscreenCanvas(canvasWidth, canvasHeight)
1837  private offContext: OffscreenCanvasRenderingContext2D = this.offscreenCanvas.getContext("2d", this.settings)
1838  public static instance: LeadingMarginCreator = new LeadingMarginCreator()
1839
1840  // 获得字体字号级别,分别是从0到4级
1841  public getFontSizeLevel(fontSize: number) {
1842    const fontScaled: number = Number(fontSize) / 16
1843
1844    enum FontSizeScaleThreshold {
1845      SMALL = 0.9,
1846      NORMAL = 1.1,
1847      LEVEL_1_LARGE = 1.2,
1848      LEVEL_2_LARGE = 1.4,
1849      LEVEL_3_LARGE = 1.5
1850    }
1851
1852    let fontSizeLevel: number = 1
1853
1854    if (fontScaled < FontSizeScaleThreshold.SMALL) {
1855      fontSizeLevel = 0
1856    } else if (fontScaled < FontSizeScaleThreshold.NORMAL) {
1857      fontSizeLevel = 1
1858    } else if (fontScaled < FontSizeScaleThreshold.LEVEL_1_LARGE) {
1859      fontSizeLevel = 2
1860    } else if (fontScaled < FontSizeScaleThreshold.LEVEL_2_LARGE) {
1861      fontSizeLevel = 3
1862    } else if (fontScaled < FontSizeScaleThreshold.LEVEL_3_LARGE) {
1863      fontSizeLevel = 4
1864    } else {
1865      fontSizeLevel = 1
1866    }
1867
1868    return fontSizeLevel
1869  }
1870  // 获得字体字号级别,分别是从0到4级
1871  public getmarginLevel(Width: number) {
1872    let marginlevel: number = 1
1873    if (Width == 40) {
1874      marginlevel = 2.0
1875    } else if (Width == 80) {
1876      marginlevel = 1.0
1877    } else if (Width == 120) {
1878      marginlevel = 2/3
1879    } else if (Width == 160) {
1880      marginlevel = 0.5
1881    } else if (Width == 200) {
1882      marginlevel = 0.4
1883    }
1884    return marginlevel
1885  }
1886
1887  public genStrMark(fontSize: number, str: string): PixelMap {
1888    this.offContext = this.offscreenCanvas.getContext("2d", this.settings)
1889    this.clearCanvas()
1890    this.offContext.font = fontSize + 'vp sans-serif'
1891    this.offContext.fillText(str + '.', 0, fontSize * 0.9)
1892    return this.offContext.getPixelMap(0, 0, fontSize * (str.length + 1) / 1.75, fontSize)
1893  }
1894
1895  public genSquareMark(fontSize: number): PixelMap {
1896    this.offContext = this.offscreenCanvas.getContext("2d", this.settings)
1897    this.clearCanvas()
1898    const coordinate = fontSize * (1 - 1 / 1.5) / 2
1899    const sideLength = fontSize / 1.5
1900    this.offContext.fillRect(coordinate, coordinate, sideLength, sideLength)
1901    return this.offContext.getPixelMap(0, 0, fontSize, fontSize)
1902  }
1903
1904  // 生成圆圈符号
1905  public genCircleMark(fontSize: number, width: number, level?: number ): PixelMap {
1906    const indentLevel = level ?? 1
1907    const offsetLevel = [22, 28, 32, 34, 38]
1908    const fontSizeLevel = this.getFontSizeLevel(fontSize)
1909    const marginlevel = this.getmarginLevel(width)
1910    const newOffContext: OffscreenCanvasRenderingContext2D = this.offscreenCanvas.getContext("2d", this.settings)
1911    const centerCoordinate = 50
1912    const radius = 10
1913    this.clearCanvas()
1914    newOffContext.ellipse(100 * (indentLevel + 1) - centerCoordinate * marginlevel, offsetLevel[fontSizeLevel], radius * marginlevel, radius, 0, 0, 2 * Math.PI)
1915    newOffContext.fillStyle = '66FF0000'
1916    newOffContext.fill()
1917    return newOffContext.getPixelMap(0, 0, 100 + 100 * indentLevel, 100)
1918  }
1919
1920  private clearCanvas() {
1921    this.offContext.clearRect(0, 0, canvasWidth, canvasHeight)
1922  }
1923}
1924
1925@Entry
1926@Component
1927struct Index {
1928  controller: RichEditorController = new RichEditorController()
1929  options: RichEditorOptions = { controller: this.controller }
1930  private leadingMarkCreatorInstance = LeadingMarginCreator.instance
1931  private fontNameRawFile: string = 'MiSans-Bold'
1932  @State fs: number = 30
1933  @State cl: number = Color.Black
1934  private leftMargin: Dimension = 0
1935  private richEditorTextStyle: RichEditorTextStyle = {}
1936
1937  aboutToAppear() {
1938    font.registerFont({
1939      familyName: 'MiSans-Bold',
1940      familySrc: '/font/MiSans-Bold.ttf'
1941    })
1942  }
1943
1944  build() {
1945    Scroll() {
1946      Column() {
1947        RichEditor(this.options)
1948          .onReady(() => {
1949            this.controller.addTextSpan("0123456789\n",
1950              {
1951                style:
1952                {
1953                  fontWeight: 'medium',
1954                  fontFamily: this.fontNameRawFile,
1955                  fontColor: Color.Red,
1956                  fontSize: 50,
1957                  fontStyle: FontStyle.Italic,
1958                  decoration: { type: TextDecorationType.Underline, color: Color.Green }
1959                }
1960              })
1961
1962            this.controller.addTextSpan("abcdefg",
1963              {
1964                style:
1965                {
1966                  fontWeight: FontWeight.Lighter,
1967                  fontFamily: 'HarmonyOS Sans',
1968                  fontColor: 'rgba(0,128,0,0.5)',
1969                  fontSize: 30,
1970                  fontStyle: FontStyle.Normal,
1971                  decoration: { type: TextDecorationType.Overline, color: 'rgba(169, 26, 246, 0.50)' }
1972                }
1973              })
1974          })
1975          .borderWidth(1)
1976          .borderColor(Color.Green)
1977          .width("100%")
1978          .height("50%")
1979
1980        Row({ space: 5 }) {
1981          Button('setTypingStyle1')
1982            .fontSize(10)
1983            .onClick(() => {
1984              this.controller.setTypingStyle(
1985                {
1986                  fontWeight: 'medium',
1987                  fontFamily: this.fontNameRawFile,
1988                  fontColor: Color.Blue,
1989                  fontSize: 50,
1990                  fontStyle: FontStyle.Italic,
1991                  decoration: { type: TextDecorationType.Underline, color: Color.Green }
1992                })
1993            })
1994
1995          Button('setTypingStyle2')
1996            .fontSize(10)
1997            .onClick(() => {
1998              this.controller.setTypingStyle(
1999                {
2000                  fontWeight: FontWeight.Lighter,
2001                  fontFamily: 'HarmonyOS Sans',
2002                  fontColor: Color.Green,
2003                  fontSize: '30',
2004                  fontStyle: FontStyle.Normal,
2005                  decoration: { type: TextDecorationType.Overline, color: 'rgba(169, 26, 246, 0.50)' }
2006                })
2007            })
2008        }
2009        Divider()
2010        Button("getTypingStyle").onClick(() => {
2011          this.richEditorTextStyle = this.controller.getTypingStyle()
2012          console.log("RichEditor getTypingStyle:" + JSON.stringify(this.richEditorTextStyle))
2013        })
2014        Divider()
2015        Row({ space: 5 }) {
2016          Button("向右列表缩进").onClick(() => {
2017            let margin = Number(this.leftMargin)
2018            if (margin < 200) {
2019              margin += Indentation
2020              this.leftMargin = margin
2021            }
2022            this.controller.updateParagraphStyle({
2023              start: -10,
2024              end: -10,
2025              style: {
2026                leadingMargin : {
2027                  pixelMap : this.leadingMarkCreatorInstance.genCircleMark(100, margin, 1),
2028                  size: [margin, 40]
2029                }
2030              }
2031            })
2032          })
2033
2034          Button("向左列表缩进").onClick(() => {
2035            let margin = Number(this.leftMargin)
2036            if (margin > 0) {
2037              margin -= Indentation
2038              this.leftMargin = margin
2039            }
2040            this.controller.updateParagraphStyle({
2041              start: -10,
2042              end: -10,
2043              style: {
2044                leadingMargin : {
2045                  pixelMap : this.leadingMarkCreatorInstance.genCircleMark(100, margin, 1),
2046                  size: [margin, 40]
2047                }
2048              }
2049            })
2050          })
2051        }
2052        Divider()
2053        Row({ space: 5 }) {
2054          Button("向右空白缩进").onClick(() => {
2055            let margin = Number(this.leftMargin)
2056            if (margin < 200) {
2057              margin += Indentation
2058              this.leftMargin = margin
2059            }
2060            this.controller.updateParagraphStyle({
2061              start: -10,
2062              end: -10,
2063              style: {
2064                leadingMargin: margin
2065              }
2066            })
2067          })
2068
2069          Button("向左空白缩进").onClick(() => {
2070            let margin = Number(this.leftMargin)
2071            if (margin > 0) {
2072              margin -= Indentation
2073              this.leftMargin = margin
2074            }
2075            this.controller.updateParagraphStyle({
2076              start: -10,
2077              end: -10,
2078              style: {
2079                leadingMargin: margin
2080              }
2081            })
2082          })
2083        }
2084      }.borderWidth(1).borderColor(Color.Red)
2085    }
2086  }
2087}
2088```
2089![UpdateParagraphAndTypingStyle](figures/richEditorUpdateParagraphAndTypingStyle.gif)
2090
2091### 示例8
2092``` ts
2093@Entry
2094@Component
2095struct Index {
2096  controller: RichEditorController = new RichEditorController();
2097  options: RichEditorOptions = { controller: this.controller };
2098  private start: number = -1;
2099  private end: number = -1;
2100  @State message: string = "[-1, -1]"
2101  @State content: string = ""
2102  @State visable :number = 0;
2103  @State index:number = 0;
2104  @State offsetx: number = 0;
2105  @State textShadows : (ShadowOptions | Array<ShadowOptions> ) =
2106    [{ radius: 10, color: Color.Red, offsetX: 10, offsetY: 0 },{ radius: 10, color: Color.Black, offsetX: 20, offsetY: 0 },
2107      { radius: 10, color: Color.Brown, offsetX: 30, offsetY: 0 },{ radius: 10, color: Color.Green, offsetX: 40, offsetY: 0 },
2108      { radius: 10, color: Color.Yellow, offsetX: 100, offsetY: 0 }]
2109  @State textshadowOf : ShadowOptions[] = []
2110  build() {
2111    Column() {
2112      Column() {
2113        Text("selection range:").width("100%")
2114        Text() {
2115          Span(this.message)
2116        }.width("100%")
2117        Text("selection content:").width("100%")
2118        Text() {
2119          Span(this.content)
2120        }.width("100%")
2121      }
2122      .borderWidth(1)
2123      .borderColor(Color.Red)
2124      .width("100%")
2125      .height("20%")
2126      Row() {
2127        Button("更新样式: 加粗 & 文本阴影").onClick(() => {
2128          this.controller.updateSpanStyle({
2129            start: this.start,
2130            end: this.end,
2131            textStyle:
2132            {
2133              fontWeight: FontWeight.Bolder,
2134              textShadow: this.textShadows
2135            }
2136          })
2137        })
2138      }
2139      .borderWidth(1)
2140      .borderColor(Color.Red)
2141      .width("100%")
2142      .height("10%")
2143      Column() {
2144        RichEditor(this.options)
2145          .onReady(() => {
2146            this.controller.addTextSpan("0123456789",
2147              {
2148                style:
2149                {
2150                  fontColor: Color.Orange,
2151                  fontSize: 30,
2152                  textShadow: { radius: 10, color: Color.Blue, offsetX: 10, offsetY: 0 }
2153                }
2154              })
2155          })
2156          .borderWidth(1)
2157          .borderColor(Color.Green)
2158          .width("100%")
2159          .height("30%")
2160      }
2161      .borderWidth(1)
2162      .borderColor(Color.Red)
2163      .width("100%")
2164      .height("70%")
2165    }
2166  }
2167}
2168```
2169
2170![TextshadowExample](figures/rich_editor_textshadow.png)
2171
2172### 示例9
2173``` ts
2174@Builder
2175function placeholderBuilder2() {
2176  Row({ space: 2 }) {
2177    Image($r("app.media.icon")).width(24).height(24).margin({ left: -5 })
2178    Text('okokokok').fontSize(10)
2179  }.width('20%').height(50).padding(10).backgroundColor(Color.Red)
2180}
2181
2182// xxx.ets
2183@Entry
2184@Component
2185struct Index {
2186  controller: RichEditorController = new RichEditorController();
2187  option: RichEditorOptions = { controller: this.controller };
2188  private start: number = 2;
2189  private end: number = 4;
2190  @State message: string = "[-1, -1]"
2191  @State content: string = ""
2192  private my_offset: number | undefined = undefined
2193  private my_builder: CustomBuilder = undefined
2194
2195  @Builder
2196  placeholderBuilder() {
2197    Row({ space: 2 }) {
2198      Image($r("app.media.icon")).width(24).height(24).margin({ left: -5 })
2199      Text('Custom Popup').fontSize(10)
2200    }.width(100).height(50).padding(5)
2201  }
2202
2203  @Builder
2204  placeholderBuilder3() {
2205    Text("hello").padding('20').borderWidth(1).width('100%')
2206  }
2207
2208  @Builder
2209  placeholderBuilder4() {
2210    Column() {
2211      Column({ space: 5 }) {
2212        Text('direction:Row').fontSize(9).fontColor(0xCCCCCC).width('90%')
2213        Flex({ direction: FlexDirection.Row }) { // 子组件在容器主抽上行布局
2214          Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)
2215          Text('1').width('20%').height(50).backgroundColor(0xD2B48C)
2216          Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)
2217          Text('1').width('20%').height(50).backgroundColor(0xD2B48C)
2218        }
2219        .height(70)
2220        .width('90%')
2221        .padding(10)
2222        .backgroundColor(0xAFEEEE)
2223
2224        Text('direction:RowReverse').fontSize(9).fontColor(0xCCCCCC).width('90%')
2225        Flex({ direction: FlexDirection.RowReverse }) { // 子组件在容器主抽上反向行布局
2226          Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)
2227          Text('1').width('20%').height(50).backgroundColor(0xD2B48C)
2228          Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)
2229          Text('1').width('20%').height(50).backgroundColor(0xD2B48C)
2230        }
2231        .height(70)
2232        .width('90%')
2233        .padding(10)
2234        .backgroundColor(0xAFEEEE)
2235
2236        Text('direction:Column').fontSize(9).fontColor(0xCCCCCC).width('90%')
2237        Flex({ direction: FlexDirection.Column }) { // 子组件在容器主抽上列布局
2238          Text('1').width('20%').height(40).backgroundColor(0xF5DEB3)
2239          Text('1').width('20%').height(40).backgroundColor(0xD2B48C)
2240          Text('1').width('20%').height(40).backgroundColor(0xF5DEB3)
2241          Text('1').width('20%').height(40).backgroundColor(0xD2B48C)
2242        }
2243        .height(160)
2244        .width('90%')
2245        .padding(10)
2246        .backgroundColor(0xAFEEEE)
2247
2248        Text('direction:ColumnReverse').fontSize(9).fontColor(0xCCCCCC).width('90%')
2249        Flex({ direction: FlexDirection.ColumnReverse }) { // 子组件在容器主抽上反向列布局
2250          Text('1').width('20%').height(40).backgroundColor(0xF5DEB3)
2251          Text('1').width('20%').height(40).backgroundColor(0xD2B48C)
2252          Text('1').width('20%').height(40).backgroundColor(0xF5DEB3)
2253          Text('1').width('20%').height(40).backgroundColor(0xD2B48C)
2254        }
2255        .height(160)
2256        .width('90%')
2257        .padding(10)
2258        .backgroundColor(0xAFEEEE)
2259      }.width('100%').margin({ top: 5 })
2260    }.width('100%')
2261  }
2262
2263  @Builder
2264  MyMenu() {
2265    Menu() {
2266      MenuItem({ startIcon: $r("app.media.icon"), content: "菜单选项1" })
2267      MenuItem({ startIcon: $r("app.media.icon"), content: "菜单选项2" })
2268        .enabled(false)
2269    }
2270  }
2271
2272  build() {
2273    Column() {
2274      Column() {
2275        Text("selection range:").width("100%")
2276        Text() {
2277          Span(this.message)
2278        }.width("100%")
2279
2280        Text("selection content:").width("100%")
2281        Text() {
2282          Span(this.content)
2283        }.width("100%")
2284      }
2285      .borderWidth(1)
2286      .borderColor(Color.Red)
2287      .width("100%")
2288      .height("20%")
2289
2290      Row() {
2291        Button("获取选择内容 getSpans").onClick(() => {
2292          console.info('getSpans='+JSON.stringify(this.controller.getSpans({ start:1, end:5 })))
2293          console.info('getParagraphs='+JSON.stringify(this.controller.getParagraphs({ start:1, end:5 })))
2294          this.content = ""
2295          this.controller.getSpans({
2296            start: this.start,
2297            end: this.end
2298          }).forEach(item => {
2299            if (typeof (item as RichEditorImageSpanResult)['imageStyle'] != 'undefined') {
2300              if ((item as RichEditorImageSpanResult).valueResourceStr == "") {
2301                console.info("builder span index " + (item as RichEditorImageSpanResult).spanPosition.spanIndex + ", range : " + (item as RichEditorImageSpanResult).offsetInSpan[0] + ", " +
2302                  (item as RichEditorImageSpanResult).offsetInSpan[1] + ", size : " + (item as RichEditorImageSpanResult).imageStyle[0] + ", " + (item as RichEditorImageSpanResult).imageStyle[1])
2303              } else {
2304                console.info("image span " + (item as RichEditorImageSpanResult).valueResourceStr + ", index : " + (item as RichEditorImageSpanResult).spanPosition.spanIndex + ", range: " +
2305                  (item as RichEditorImageSpanResult).offsetInSpan[0] + ", " + (item as RichEditorImageSpanResult).offsetInSpan[1] + ", size : " +
2306                  (item as RichEditorImageSpanResult).imageStyle.size[0] + ", " + (item as RichEditorImageSpanResult).imageStyle.size[1])
2307              }
2308            } else {
2309              this.content += (item as RichEditorTextSpanResult).value;
2310              this.content += "\n"
2311              console.info("text span: " + (item as RichEditorTextSpanResult).value)
2312            }
2313          })
2314        })
2315        Button("获取选择内容 getSelection").onClick(() => {
2316          this.content = "";
2317          let select = this.controller.getSelection()
2318          console.info("selection start " + select.selection[0] + " end " + select.selection[1])
2319          select.spans.forEach(item => {
2320            if (typeof (item as RichEditorImageSpanResult)['imageStyle'] != 'undefined') {
2321              if ((item as RichEditorImageSpanResult).valueResourceStr == "") {
2322                console.info("builder span index " + (item as RichEditorImageSpanResult).spanPosition.spanIndex + ", range : " + (item as RichEditorImageSpanResult).offsetInSpan[0] + ", " +
2323                  (item as RichEditorImageSpanResult).offsetInSpan[1] + ", size : " + (item as RichEditorImageSpanResult).imageStyle[0] + ", " + (item as RichEditorImageSpanResult).imageStyle[1])
2324              } else {
2325                console.info("image span " + (item as RichEditorImageSpanResult).valueResourceStr + ", index : " + (item as RichEditorImageSpanResult).spanPosition.spanIndex + ", range: " +
2326                  (item as RichEditorImageSpanResult).offsetInSpan[0] + ", " + (item as RichEditorImageSpanResult).offsetInSpan[1] + ", size : " +
2327                  (item as RichEditorImageSpanResult).imageStyle.size[0] + ", " + (item as RichEditorImageSpanResult).imageStyle.size[1])
2328              }
2329            } else {
2330              this.content += (item as RichEditorTextSpanResult).value;
2331              this.content += "\n"
2332              console.info("text span: " + (item as RichEditorTextSpanResult).value)
2333            }
2334          })
2335        })
2336        Button("删除选择内容").onClick(() => {
2337          this.controller.deleteSpans({
2338            start: this.start,
2339            end: this.end
2340          })
2341        })
2342      }
2343      .borderWidth(1)
2344      .borderColor(Color.Red)
2345      .width("100%")
2346      .height("10%")
2347
2348      Column() {
2349        RichEditor(this.option)
2350          .onReady(() => {
2351            this.controller.addTextSpan("0123456789",
2352              {
2353                style:
2354                {
2355                  fontColor: Color.Orange,
2356                  fontSize: 30
2357                }
2358              })
2359            this.controller.addImageSpan($r("app.media.icon"),
2360              {
2361                imageStyle:
2362                {
2363                  size: ["57px", "57px"]
2364                }
2365              })
2366          })
2367          .onSelect((value: RichEditorSelection) => {
2368            this.start = value.selection[0];
2369            this.end = value.selection[1];
2370            this.message = "[" + this.start + ", " + this.end + "]"
2371            console.info("onSelect="+JSON.stringify(value))
2372          })
2373          .aboutToIMEInput((value: RichEditorInsertValue) => {
2374            console.log("---------------------- aboutToIMEInput --------------------")
2375            console.info("aboutToIMEInput="+JSON.stringify(value))
2376            console.log("insertOffset:" + value.insertOffset)
2377            console.log("insertValue:" + value.insertValue)
2378            return true;
2379          })
2380          .onIMEInputComplete((value: RichEditorTextSpanResult) => {
2381            console.log("---------------------- onIMEInputComplete --------------------")
2382            console.info("onIMEInputComplete="+JSON.stringify(value))
2383            console.log("spanIndex:" + value.spanPosition.spanIndex)
2384            console.log("spanRange:[" + value.spanPosition.spanRange[0] + "," + value.spanPosition.spanRange[1] + "]")
2385            console.log("offsetInSpan:[" + value.offsetInSpan[0] + "," + value.offsetInSpan[1] + "]")
2386            console.log("value:" + value.value)
2387          })
2388          .aboutToDelete((value: RichEditorDeleteValue) => {
2389            value.richEditorDeleteSpans.forEach(item => {
2390              console.log("---------------------- item --------------------")
2391              console.info("spanIndex=" + item.spanPosition.spanIndex)
2392              console.log("spanRange:[" + item.spanPosition.spanRange[0] + "," + item.spanPosition.spanRange[1] + "]")
2393              console.log("offsetInSpan:[" + item.offsetInSpan[0] + "," + item.offsetInSpan[1] + "]")
2394              if (typeof (item as RichEditorImageSpanResult)['imageStyle'] != 'undefined') {
2395                if ((item as RichEditorImageSpanResult).valueResourceStr == "") {
2396                  console.info("builder span index " + (item as RichEditorImageSpanResult).spanPosition.spanIndex + ", range : " + (item as RichEditorImageSpanResult).offsetInSpan[0] + ", " +
2397                  (item as RichEditorImageSpanResult).offsetInSpan[1] + ", size : " + (item as RichEditorImageSpanResult).imageStyle[0] + ", " + (item as RichEditorImageSpanResult).imageStyle[1])
2398                } else {
2399                  console.info("image span " + (item as RichEditorImageSpanResult).valueResourceStr + ", index : " + (item as RichEditorImageSpanResult).spanPosition.spanIndex + ", range: " +
2400                  (item as RichEditorImageSpanResult).offsetInSpan[0] + ", " + (item as RichEditorImageSpanResult).offsetInSpan[1] + ", size : " +
2401                  (item as RichEditorImageSpanResult).imageStyle.size[0] + ", " + (item as RichEditorImageSpanResult).imageStyle.size[1])
2402                }
2403              } else {
2404                console.info("delete text: " + (item as RichEditorTextSpanResult).value)
2405              }
2406            })
2407            return true;
2408          })
2409          .borderWidth(1)
2410          .borderColor(Color.Green)
2411          .width("100%")
2412          .height("30%")
2413
2414        Button("add span")
2415          .onClick(() => {
2416            let num = this.controller.addBuilderSpan(this.my_builder, { offset: this.my_offset })
2417            console.info('addBuilderSpan return ' + num)
2418          })
2419        Button("add image")
2420          .onClick(() => {
2421            let num = this.controller.addImageSpan($r("app.media.icon"), {
2422              imageStyle: {
2423                size: ["50px", "50px"],
2424                verticalAlign: ImageSpanAlignment.BOTTOM,
2425                layoutStyle: {
2426                  borderRadius: undefined,
2427                  margin: undefined
2428                }
2429              }
2430            })
2431            console.info('addImageSpan return' + num)
2432          })
2433        Row() {
2434          Button('builder1').onClick(() => {
2435            this.my_builder = () => {
2436              this.placeholderBuilder()
2437            }
2438          })
2439          Button('builder2').onClick(() => {
2440            this.my_builder = placeholderBuilder2.bind(this)
2441          })
2442          Button('builder3').onClick(() => {
2443            this.my_builder = () => {
2444              this.placeholderBuilder3()
2445            }
2446          })
2447          Button('builder4').onClick(() => {
2448            this.my_builder = () => {
2449              this.placeholderBuilder4()
2450            }
2451          })
2452        }
2453      }
2454      .borderWidth(1)
2455      .borderColor(Color.Red)
2456      .width("100%")
2457      .height("70%")
2458    }
2459  }
2460}
2461```
2462![AddBuilderSpanExample](figures/rich_editor_addBuilderSpan.png)
2463
2464### 示例10
2465enableDataDetector和dataDetectorConfig使用示例
2466
2467```ts
2468@Entry
2469@Component
2470struct TextExample7 {
2471  controller: RichEditorController = new RichEditorController();
2472  options: RichEditorOptions = { controller: this.controller };
2473  @State phoneNumber: string = '(86) (755) ********';
2474  @State url: string = 'www.********.com';
2475  @State email: string = '***@example.com';
2476  @State address: string = 'XX省XX市XX区XXXX';
2477  @State enableDataDetector: boolean = true;
2478  @State types: TextDataDetectorType[] = [];
2479
2480  build() {
2481    Row() {
2482      Column() {
2483        RichEditor(this.options)
2484          .onReady(() => {
2485            this.controller.addTextSpan('电话号码:' + this.phoneNumber + '\n',
2486              {
2487                style:
2488                {
2489                  fontSize: 30
2490                }
2491              })
2492            this.controller.addTextSpan('链接:' + this.url + '\n',
2493              {
2494                style:
2495                {
2496                  fontSize: 30
2497                }
2498              })
2499            this.controller.addTextSpan('邮箱:' + this.email + '\n',
2500              {
2501                style:
2502                {
2503                  fontSize: 30
2504                }
2505              })
2506            this.controller.addTextSpan('地址:' + this.address,
2507              {
2508                style:
2509                {
2510                  fontSize: 30
2511                }
2512              })
2513          })
2514          .copyOptions(CopyOptions.InApp)
2515          .enableDataDetector(this.enableDataDetector)
2516          .dataDetectorConfig({types : this.types, onDetectResultUpdate: (result: string)=>{}})
2517          .borderWidth(1)
2518          .padding(10)
2519          .width('100%')
2520      }
2521      .width('100%')
2522    }
2523  }
2524}
2525```
2526### 示例11
2527preventDefault使用示例
2528```ts
2529@Entry
2530@Component
2531struct RichEditorDemo {
2532  controller: RichEditorController = new RichEditorController();
2533  options: RichEditorOptions = { controller: this.controller };
2534
2535  build() {
2536    Column({ space: 2 }) {
2537      RichEditor(this.options)
2538        .onReady(() => {
2539          this.controller.addTextSpan('RichEditor preventDefault')
2540        })
2541        .onPaste((event?: PasteEvent) => {
2542          if (event != undefined && event.preventDefault) {
2543            event.preventDefault();
2544          }
2545        })
2546        .borderWidth(1)
2547        .borderColor(Color.Green)
2548        .width('100%')
2549        .height('40%')
2550    }
2551  }
2552}
2553```
2554![PreventDefaultExample](figures/richEditorPreventDefault.gif)