富文本编辑:基于TextInput的富文本编辑器开发(80)

富文本编辑:基于TextInput的富文本编辑器开发(80) 在鸿蒙HarmonyOS应用开发中首先需要明确一个核心的技术选型前提TextInput组件仅支持单行文本输入且不支持富文本样式。因此开发富文本编辑器不能基于TextInput而必须使用鸿蒙原生提供的RichEditor组件。结合鸿蒙 ArkUI 的特性以下是基于RichEditor构建富文本编辑器的核心架构与实战代码一、 基础架构RichEditor 与控制器绑定RichEditor组件的核心在于RichEditorController它负责控制编辑器的样式、内容插入及状态获取。同时配合TextInput处理单行标题输入可构建出完整的笔记编辑界面。核心代码示例Entry Component struct NoteEditor { State noteTitle: string ; controller: RichEditorController new RichEditorController(); build() { Column({ space: 16 }) { // 1. 标题输入使用 TextInput TextInput({ placeholder: 请输入标题, text: this.noteTitle }) .fontSize(24) .fontWeight(FontWeight.Bold) .onChange(value this.noteTitle value) // 2. 富文本编辑区使用 RichEditor RichEditor(this.controller) .placeholder(开始编写内容...) .width(100%) .height(70%) .border({ width: 1, color: #E0E0E0 }) .borderRadius(8) .padding(12) } .padding(16) } }二、 核心交互工具栏与样式控制富文本编辑器的灵魂在于工具栏与编辑器的联动。通过监听onSelect事件获取当前选中文本的状态并调用 Controller 的 API如addTextSpan、insertText来应用格式。核心代码示例// 工具栏构建 Builder buildToolbar() { Row({ space: 12 }) { Button(B).onClick(() { // 注意aboutToIMEInput 仅对接下来输入的文本生效 // 若要修改已选中文本需先 getSelection() 再 setStyle() this.controller.addTextSpan(, { fontWeight: FontWeight.Bold }); }) Button(I).onClick(() { this.controller.addTextSpan(, { fontStyle: FontStyle.Italic }); }) Button(插入图片).onClick(() { // 在光标位置插入图片 this.controller.insertImage({ src: $r(app.media.icon), width: 100, height: 100, position: RichEditorInsertPosition.CURSOR }); }) } }三、 进阶能力内容序列化与持久化存储编辑完成后需要将带有样式的富文本内容保存下来。RichEditor支持导出为AttributedString对象该对象包含了文本内容和所有样式信息可序列化为二进制格式存储。核心代码示例// 保存笔记 async function saveNote() { // 获取带样式的文本对象 const attributedString await this.controller.getAttributedText(); // 序列化为二进制/字符串格式 const serializedData attributedString.serialize(); // 存入数据库建议使用 BLOB 字段保存二进制格式避免 JSON 丢失样式 await saveToFile(serializedData); } // 加载已保存的笔记 async function loadNote(fileContent: string) { // 反序列化并还原编辑器内容 const attributedString AttributedString.deserialize(fileContent); this.controller.setText(attributedString); }四、 桌面级体验性能优化在 PC 端或处理长文档时富文本编辑器面临极大的性能挑战。鸿蒙RichEditor提供了底层优化机制开发者在实战中需注意以下规范大文档渲染优化RichEditor内部采用了虚拟滚动技术和增量更新机制仅渲染可视区域内容并在编辑时只更新变化部分避免全量重绘。大文本分块加载处理超长文本时建议采用分块加载策略如每页不超过 5000 字符图片插入前务必进行压缩处理防止内存溢出。键盘遮挡问题在移动端或特定窗口布局下若遇到键盘遮挡内容可使用.customKeyboard()自定义键盘或开启avoidKeyboard: true属性实现自动避让。安全与权限限制出于安全考虑RichEditor会自动过滤script和iframe等危险标签。若需支持复制粘贴功能必须在module.json5中声明ohos.permission.PASTE权限。五、 进阶交互工具栏与编辑器状态的双向联动在真实的编辑器中当用户通过鼠标选中一段已有的文本时工具栏的按钮如加粗、斜体必须高亮显示当前状态。这需要监听onSelect事件并结合getSpansAPI 动态更新工具栏的状态变量。核心代码示例RichEditor(this.controller) .onSelect((selection: RichEditorSelection) { // 获取当前选中区域的 Span 信息 const spans this.controller.getSpans({ start: selection.start, end: selection.end }); // 遍历判断是否包含粗体样式从而驱动工具栏 UI 更新 this.isBoldActive spans.some(span span.style?.fontWeight FontWeight.Bold); })六、 内容扩展嵌入自定义组件BuilderSpan除了常规的图文混排现代富文本编辑器通常需要支持插入代码块、自定义图表或文件附件。通过addBuilderSpanAPI可以将任意Builder修饰的自定义 UI 组件无缝嵌入到文本流中。核心代码示例Builder CodeBlockBuilder() { Column() { Text(console.log(Hello HarmonyOS);) .fontFamily(monospace) .fontColor(Color.White) } .width(100%) .padding(12) .backgroundColor(#282C34) .borderRadius(8) } // 在工具栏触发插入 Button(插入代码块).onClick(() { this.controller.addBuilderSpan(() { this.CodeBlockBuilder() }); })七、 桌面级体验自定义文本选择菜单Edit MenuPC 端用户习惯在选中文本后通过右键或长按呼出菜单。除了系统默认的复制、粘贴开发者可以通过editMenuOptions扩展自定义操作如AI 润色、翻译、搜索。核心代码示例RichEditor(this.controller) .editMenuOptions({ onCreateMenu: (textMenuItems: ArrayTextMenuItem) { // 向系统菜单追加自定义选项 textMenuItems.push({ content: AI 润色, id: TextMenuItemId.of(ai_polish), icon: $r(app.media.ai_icon) }); return textMenuItems; }, onMenuItemClick: (item: TextMenuItem, range: TextRange) { if (item.id?.toString() ai_polish) { const selectedSpans this.controller.getSpans(range); // 触发 AI 润色逻辑... } return true; } })八、 智能化增强实体识别与预上屏API 12鸿蒙系统为RichEditor提供了强大的底层智能能力。开启enableDataDetector后编辑器能自动识别文本中的电话号码、URL 链接并添加可点击的下划线结合enablePreviewText还能实现输入法候选词的实时预览大幅提升输入体验。核心代码示例RichEditor(this.controller) // 自动识别链接和电话 .enableDataDetector(true) .dataDetectorConfig({ types: [DetectType.PHONE, DetectType.URL], onDetectResultUpdate: (result) { console.info(识别到实体类型: ${result.type}); } }) // 开启输入法候选词预上屏 .enablePreviewText(true) .onIMEInputComplete((result) { console.info(最终确认输入: ${result.text}); })区分行内样式与段落样式在调用 Controller API 时需注意加粗、颜色等属于行内样式需通过aboutToIMEInput影响后续输入或通过setStyle修改选中文本而左对齐、无序列表等属于段落样式应使用setTextAlign或setStyle({ applyList: ... })进行控制。大文本内存管理虽然RichEditor内部有虚拟滚动优化但在处理超长文档时仍建议在业务层实现“分块加载”。每次仅向编辑器注入 5000 字符左右的内容滚动到底部时再动态追加避免内存飙升。图片异步压缩在通过insertImage或addImageSpan插入本地相册图片时切勿直接将原图 URI 传入。应先在后台线程TaskPool对图片进行尺寸压缩与格式转换再插入编辑器否则极易导致编辑器渲染卡顿或 OOM。撤销/重做Undo/Redo机制RichEditorController内置了历史记录栈。在构建工具栏时可直接绑定controller.onDo()撤销和controller.reDo()重做方法并结合currentIndex与historyRecordArray.length来动态控制按钮的禁用状态。