1. 项目概述:HarmonyOS RichEditor实现@功能的核心挑战
深夜赶工团队协作应用时,产品经理指着设计稿要求实现"任务评论中能@同事"的功能。这个看似简单的需求背后隐藏着HarmonyOS富文本编辑器的独特设计哲学。当我第一次尝试用RichEditor组件实现这个功能时,遇到了几个令人抓狂的问题:
- 删除不整体:用户输入"@张三"后,按删除键需要按三次才能完全删除(先删"三",再删"张",最后删"@")
- 内容获取不全:调用接口获取文本时,@人名神秘消失,只留下普通文本
- 光标定位异常:在@人名中间点击想修改时,光标总是跳到开头或末尾
- 换行布局错乱:长用户名换行时导致光标高度异常,显示不全
这些问题的本质在于,开发者容易把RichEditor当作一个"高级文本框",而实际上它是一个结构化文档编辑器。理解这个根本差异,是解决所有问题的关键。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术原理:RichEditor的文档模型与BuilderSpan
2.1 RichEditor的文档结构解析
RichEditor内部维护的不是简单的字符串,而是一个由多种Span组成的线性序列。每个Span都有自己的类型和属性:
typescript复制[普通文本Span "你好,"]
[BuilderSpan "@张三"] // 这是一个不可分割的整体单元
[普通文本Span "请查收"]
这种结构类似于HTML文档中的DOM树,每个元素都是独立的节点。BuilderSpan是最灵活的一种Span类型,它允许通过@Builder函数自定义渲染内容,这正是我们实现高亮@人名的关键。
2.2 整体删除的机制原理
当进行删除操作时,RichEditor是以Span为粒度处理的:
- 默认文本Span:每个字符是独立单元,删除"张三"需要两次操作
- BuilderSpan:被视为原子单元,一次删除操作就能移除整个Span
这里的关键区别在于:如果只是插入样式化的普通文本"张三",它仍然是普通文本Span,不会享受整体删除的特性。必须使用addBuilderSpan方法插入的内容才能获得这个特性。
2.3 内容获取的特殊性
RichEditor的.value或常规文本回调只包含普通文本输入。通过addBuilderSpan添加的自定义内容不会自动反映在这些接口中。这是因为:
- 富文本编辑器需要区分"显示内容"和"数据内容"
- BuilderSpan可能包含复杂的自定义渲染逻辑
- 业务数据(如用户ID)需要与显示内容分离存储
解决方案是使用RichEditorController的getSpans方法获取指定范围内的所有Span信息,同时自己维护一个数组记录对应的业务数据。
3. 完整实现方案
3.1 数据结构设计
首先需要定义清晰的数据模型来管理用户信息和Span映射:
typescript复制interface User {
id: string;
avatar: ResourceStr;
nickname: string;
}
interface RichEditorSpanClass {
value?: string;
resourceValue?: ResourceStr;
type: 'text' | 'image' | 'buil
