RichEditor 富文本实操:图文混排、超链接、自定义样式控制

一、前言

在很多业务场景里,我们都需要富文本编辑能力:公告编辑、工单备注、内容发帖、文档草稿等。ArkUI 提供的 RichEditor 组件,是官方原生富文本控件,不用额外引入第三方 Web 富文本,能直接在页面内完成文字编辑、插入图片、添加链接、修改文字样式。

不少同学初次上手 RichEditor,容易踩坑:图片插入后布局错乱、超链接点击不生效、样式修改只选中部分文字不生效、数据回显丢失格式。这些问题大多是对组件数据模型、编辑范围接口不熟悉导致。本文基于最新鸿蒙 API,讲解 RichEditor 核心用法,附带可直接运行完整代码。

二、RichEditor 基础概念

RichEditor 的内容由RichEditorSpan构成,一段富文本可以包含文本、图片、链接等不同类型的 Span。

  • TextSpan:普通文本片段,控制字体、颜色、字号、粗斜体
  • ImageSpan:图片片段,承载图文混排能力
  • UrlSpan:超链接片段,可绑定跳转地址

所有的样式操作,都要先获取当前光标选中范围TextRange,再针对选中区域追加或者修改 Span 属性。RichEditor 本身是可编辑组件,用户可以直接在界面上输入、选中文字。

三、基础页面搭建

先搭建基础页面,放置 RichEditor 与一组操作按钮,用来切换样式、插入图片、添加链接。

bash 复制代码
@Entry
@Component
struct RichEditorDemo {
  @State richController: RichEditorController = new RichEditorController()
  @State content: RichEditorSpan[] = [
    new RichEditorTextSpan("欢迎使用RichEditor富文本编辑器\n", {fontSize: 18, fontColor: "#222222", fontWeight: FontWeight.Bold}),
    new RichEditorTextSpan("原生组件实现图文混排、超链接与自定义样式\n", {fontSize: 14, fontColor: "#666666"})
  ]

  build() {
    Column() {
      // 操作工具栏
      Row() {
        Button("加粗").onClick(() => this.setBold())
        Button("变色").onClick(() => this.setTextColor())
        Button("插入图片").onClick(() => this.insertImage())
        Button("添加链接").onClick(() => this.insertUrl())
      }
      .width("100%")
      .margin({bottom: 12})
      .justifyContent(FlexAlign.SpaceAround)

      RichEditor(this.content, this.richController)
        .width("100%")
        .height(400)
        .border({width:1, color:"#eeeeee"})
        .placeholder("请输入富文本内容...")
    }
    .padding(16)
    .width("100%")
  }

1. 文字加粗实现

获取当前选中区域,新建文本 Span 覆盖选中范围,设置 fontWeight。

bash 复制代码
  setBold() {
    const range: TextRange = this.richController.getCaretOffset()
    if (range.start === range.end) return
    const selectSpans = this.richController.getSpansByRange(range)
    selectSpans.forEach(span => {
      if (span instanceof RichEditorTextSpan) {
        span.fontWeight = FontWeight.Bold
      }
    })
  }
  1. 修改文字颜色
bash 复制代码
  setTextColor() {
    const range: TextRange = this.richController.getCaretOffset()
    if (range.start === range.end) return
    const spans = this.richController.getSpansByRange(range)
    spans.forEach(span => {
      if (span instanceof RichEditorTextSpan) {
        span.fontColor = "#0066FF"
      }
    })
  }

3. 插入图片,图文混排

示例使用资源目录图片,替换成沙箱文件路径即可实现拍照 / 相册图片插入。

bash 复制代码
  insertImage() {
    const caretPos = this.richController.getCaretOffset().start
    const imgSpan = new RichEditorImageSpan($r("sys.media.ohos_ic_public_photo"), {
      imageWidth: 200,
      imageHeight: 140
    })
    this.richController.insertSpan(caretPos, imgSpan)
  }
  1. 插入超链接
bash 复制代码
  insertUrl() {
    const range: TextRange = this.richController.getCaretOffset()
    if (range.start === range.end) return
    const urlSpan = new RichEditorUrlSpan("点击访问官网", "https://www.harmonyos.com", {
      fontColor: "#0066FF",
      textDecoration: TextDecoration.Underline
    })
    this.richController.replaceSpan(range, urlSpan)
  }
}

四、富文本序列化与回显

实际业务中,需要把富文本内容保存、页面刷新后重新渲染。RichEditorSpan 数组可以直接转为 JSON 存储,读取后还原 Span 数组。

bash 复制代码
// 导出富文本内容,保存到持久化存储
saveRichContent() {
  const dataStr = JSON.stringify(this.content)
  // 这里可以写入Preferences/RDB
  console.info("富文本数据", dataStr)
}

// 读取JSON,回显富文本
restoreRichContent(jsonStr: string) {
  const rawData = JSON.parse(jsonStr)
  this.content = rawData.map((item: Record<string,any>) => {
    if(item.type === "text"){
      return new RichEditorTextSpan(item.text, item.style)
    }else if(item.type === "image"){
      return new RichEditorImageSpan(item.imageValue, item.style)
    }else if(item.type === "url"){
      return new RichEditorUrlSpan(item.text, item.url, item.style)
    }
  })
}

五、小结

RichEditor 原生富文本组件,省去 WebView 方案带来的包体积、通信调试成本。核心思路就是通过RichEditorController获取光标选区,增改 TextSpan、ImageSpan、UrlSpan 实现图文、链接、自定义样式。 基础的编辑、内容导出、回显逻辑可以直接复用上面代码,在此基础

相关推荐
浪遏1 小时前
D2C 系统架构复盘:从设计稿到代码,我们踩过的坑和最终方案
前端·javascript·后端
光影少年1 小时前
Fabric渲染流程
前端·react native·react.js
IT_陈寒1 小时前
我的JavaScript代码为啥在forEach里没按预期执行?
前端·人工智能·后端
再吃一根胡萝卜1 小时前
从零实现一个带虚拟滚动的 Select
前端
farerboy1 小时前
WEB 项目如何禁用 F12 等功能
前端·vue.js·架构
Epat1 小时前
一个轻量级 AI 代理工具箱,与coding plan 推荐
前端·ai编程
计算机魔术师1 小时前
持股2%却要掌舵50.1%:Anthropic上市前的控制权保卫战
前端
粥里有勺糖1 小时前
体验一下最近比较🔥的闪卡SKILL
前端·github
flash俊杰1 小时前
WebGL 实时合成管线:纹理、Shader 滤镜链与离屏渲染调度
前端