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