HarmonyOS鸿蒙Next中如何实现一个功能丰富的富文本编辑器?
HarmonyOS鸿蒙Next中如何实现一个功能丰富的富文本编辑器?
RichEditor管理内容有“基于属性字符串”和“基于Span”两种主要方式。在需要频繁交互(如发布动态)的场景下,后者通常更合适。但是,如何让这些可交互的“Span”(如一个@好友的名字)在光标移动、删除操作时能被整体对待,并且还能携带好友ID等额外数据?
这种 @好友 不建议只当普通字符串处理,最好把它设计成“原子节点 + 数据模型”。UI 上可以用 RichEditor 的 BuilderSpan/自定义 Span 展示,业务侧维护一份结构化数组,例如 { type: ‘mention’, uid, name, start, end }。
关键点有三个:
-
插入时记录 mention 的 uid/name 和当前范围,不只存显示文本。
-
删除/退格时监听删除前后的光标和范围,如果命中 mention 区间,就一次性删除整个节点,而不是让光标进入 @名字 中间。
-
提交内容时不要只读纯文本,建议序列化为“文本片段 + mention 节点”的结构;服务端保存 uid,展示时再还原成 BuilderSpan 或普通文本。
如果只是静态展示,用 TextSpan 够用;但发布动态、编辑、删除、携带好友 ID 这种交互,BuilderSpan + 独立数据模型会更稳。
更多关于HarmonyOS鸿蒙Next中如何实现一个功能丰富的富文本编辑器?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
有好用的数学公式文本三方库吗?
ArkUI组件RichEditor,这些都可以实现。而且官方给了非常多的《示例》。
像这种@好友的,可用RichEditorController.addTextSpan()和RichEditorController.addBuilderSpan()。
如果addTextSpan,先定义个TextSpan。
interface TextSpan {
value: string;//值
spanRange: [number, number]; //起止位置
type: string;//类型如 contact
data: ESObject;//记录好友ID等
}
private textSpans: TextSpan[] = [];
删除整体就考虑用addBuilderSpan,在aboutToDelete监听下。示例参考:《自定义布局Span》。
有轮子,别自己做了。
参考组件:OpenHarmony三方库中心仓
这个场景其实已经不是“普通富文本”了,而是:
结构化富文本编辑器
本质上:
@用户
#话题
链接
卡片
表情
图片
这些都应该被当成:
“原子节点”
而不是普通字符串。
——
所以如果你用:
StyledString
或者:
纯 TextSpan
后期一定会崩。
因为:
- 删除不好处理
- 光标会跑进 @名字 中间
- 无法整体选中
- 无法挂 metadata
- 无法携带 uid/topicId
——
目前 HarmonyOS RichEditor 里,
真正适合做:
@好友
话题
卡片
复杂富文本
的方案其实是:
BuilderSpan + 自定义数据模型
不是普通 TextSpan。
——
推荐架构:
一、UI层:BuilderSpan
例如:
this.controller.addBuilderSpan(
this.buildMentionSpan(user)
)
BuilderSpan 本质上是:
“可交互组件”
不是普通文本。
所以:
- 可以整体删除
- 可以整体选中
- 可以点击
- 可以自定义背景
- 可以携带数据
- 可以完全自定义行为
官方也明确推荐复杂交互用 Span/BuilderSpan。
——
二、数据层:不要依赖 RichEditor 内容
真正重要。
很多人会犯一个错误:
把 RichEditor 当数据源
这是错的。
RichEditor:
应该只是 View
真正的数据:
你必须自己维护。
例如:
type RichNode =
| TextNode
| MentionNode
| TopicNode
| ImageNode
例如:
interface MentionNode {
type: 'mention'
uid: string
nickname: string
}
——
然后:
RichEditor 只是渲染这些节点
不是存储数据。
——
三、删除“整体删除”
这个是核心。
例如:
@张三
用户按删除:
你不应该删除:
三
而应该:
整个 MentionNode 删除
——
实现方式:
监听:
.onSelectionChange()
以及:
.onDeleteComplete()
然后:
自己维护 Span Range
例如:
interface SpanRange {
start: number
end: number
data: MentionNode
}
当光标进入:
[start, end]
范围:
直接:
整块删除
——
这其实和:
iOS NSAttributedString
Android SpannableString
的实现思路一样。
——
四、不要让光标进入 Mention 内部
这是重点。
例如:
@张三
光标不应该:
@
张
三
之间移动。
而应该:
整个 Span 当成一个字符
——
目前 RichEditor 没有:
真正的 atomic span
机制。
所以:
工程上一般这么干:
在 Mention 后面:
额外插入一个空格
例如:
@张三·
(最后那个不可见空格)
——
然后:
光标只允许停在:
Mention 前
Mention 后
不允许进入中间。
——
五、额外数据怎么存?
不要存在 UI。
而是:
Map<spanId, metadata>
例如:
{
spanId: "mention_1001",
uid: "u123",
nickname: "张三"
}
BuilderSpan 只负责显示:
@张三
真正业务数据:
自己存。
——
六、发布时怎么序列化?
推荐:
[
{
"type": "text",
"text": "你好"
},
{
"type": "mention",
"uid": "123",
"nickname": "张三"
}
]
不要直接存 HTML。
也不要存:
@张三
纯字符串。
否则后面:
- 改昵称
- 点击跳转
- 消息通知
- @提醒
都会出问题。
——
七、为什么“基于 Span”更适合?
因为:
富交互编辑器
本质是“节点编辑器”
不是字符串编辑器。
所以:
属性字符串
更适合:
- 富文本展示
- 静态排版
而:
Span/BuilderSpan
更适合:
- 社交编辑器
- IM
- 评论
- 发帖
- 动态发布
你好,参考最佳实践富文本编辑器, 使用RichEditor组件,实现自定义表情、@好友、添加话题等功能,并提供示例代码详细拆解细节逻辑,如@好友如何被视为一个整体,编辑器中内容如何获取并归一化处理等。

在HarmonyOS Next中,可使用ArkUI的RichEditor组件实现富文本编辑。通过设置textStyle、插入ImageSpan、LinkSpan、CustomSpan等扩展功能,支持字体样式、图片、链接、表格等。结合onEditing、onChange事件监听内容变化,利用controller进行程序化操作。
可以使用 StyledString + PlaceholderSpan 实现“整体不可拆分”的 @好友 Span,并携带额外数据(如好友 ID)。光标移动时会自动将它视为一个整体,按 Delete 键会整体删除,点击时通过 RichEditor 的 onClick 拿到数据即可。
核心步骤:
- 构建
StyledString,使用controller.setStyledString()。 - 创建
PlaceholderSpan用于 @好友,并通过PlaceholderSpan的onClick回调或StyledString的setData存入好友 ID。 - 设置
RichEditor的onClick事件,用getSpanAtOffset获取当前点击的 Span,再读取数据。
示例关键代码(简化):
let styledStr = new StyledString();
styledStr.appendString("这是一条动态");
styledStr.appendPlaceholder(new PlaceholderSpan({
width: 100, height: 30,
content: { type: "mention", id: "123" } // 携带数据
}));
this.richEditor.setStyledString(styledStr);
this.richEditor.onClick((event) => {
let selection = this.richEditor.getSelection();
let spanInfo = this.richEditor.getSpanAtOffset(selection.start);
if (spanInfo && spanInfo.span instanceof PlaceholderSpan) {
let data = spanInfo.span.content; // 取出好友 ID
}
});
该方式同时满足整体删除、光标跳转与携带数据的需要,适用于频繁交互场景。

