鸿蒙 ArkUI 组件深水区:Span 富文本嵌套,一行 Text 塞下「红蓝斜下大」五样式 + 动态高亮

写在前面

如果你写过鸿蒙 ArkUI 应用,大概率遇到过这个场景:

你写个用户协议页,一行里有「普通文本 + 蓝色协议链接 + 加粗提示 + 下划线强调」四种样式。你用三个 Text 拼------结果换行错位、行高不齐、点击事件还分三份。 你查文档发现鸿蒙有个 Span------Text 容器内嵌的富文本片段,每个 Span 单独设样式 + onClick。你点进去发现 API 一脸懵。

这是「多 Text 拼」和「Span 嵌套」的分水岭。鸿蒙给的富文本答案是 Span ------Text() { Span()...Span()... } 内嵌多个样式片段,一行对齐、单独样式、单独点击。

本文就用一个真机可跑 的「基础 Span 五样式 + Span 点击 + Span emoji + Span 动态样式」demo,把 Span 从「听名字一脸懵」讲到「下个项目直接抄」。代码托管在 AtomGit,文末有链接,真机实拍截图作证。这是组件深水区系列篇。

适合人群:写过鸿蒙应用、被「一行多样式」折磨过的同学。 不适合人群:还在学 @State 的同学------出门左转看我的入门篇。


一、先讲清楚:Span 到底是啥

一句话:Span 是鸿蒙 Text 容器内嵌的富文本片段,每个 Span 单独设样式 + onClick。

你之前写前端 <span style="color:red">红</span> 是浏览器宿主标签------鸿蒙不是浏览器环境,没 HTML 标签。Span 是鸿蒙专门给富文本的原生组件,能力对标前端的 <span> 但要在 Text() { } 内嵌使用。

核心 API 一览:

API 作用 一句话理解
Text() { Span()...Span()... } Text 内嵌多个 Span 「Text 是容器,Span 是片段」
Span('内容') 设文本内容 「这个片段写啥」
.fontColor()/.fontSize()/.fontWeight()/.fontStyle() Span 单独样式 「这个片段啥样式」
.decoration({type, color}) 下划线/删除线 「TextDecorationType.Underline/LineThrough」
.onClick() Span 单独点击 「点这个片段触发啥」

记住这六个,往下看。


二、动手:一个 Span 四姿势的 demo

2.1 基础 Span:Text 内嵌五样式片段

typescript 复制代码
Text() {
  Span('这是普通文本,')
  Span('红色加粗').fontColor('#F44336').fontWeight(FontWeight.Bold)
  Span(',')
  Span('蓝色斜体').fontColor('#2196F3').fontStyle(FontStyle.Italic)
  Span(',')
  Span('带下划线').decoration({ type: TextDecorationType.Underline, color: '#000' })
  Span(',')
  Span('20号大字').fontSize(20)
}

三个细节:

  1. Text() { Span()...Span()... }------Text 用 { } 内嵌多个 Span,不是字符串拼接
  2. 每个 Span('内容') 单独设样式------.fontColor / .fontWeight / .fontStyle / .fontSize
  3. .decoration({type, color}) 设下划线/删除线------TextDecorationType.Underline / LineThrough

这是 Span 最核心的能力------一行内嵌多个样式片段,行高对齐、不换行。

2.2 Span onClick:单独点击事件

typescript 复制代码
Text() {
  Span('点这个 ')
  Span('高亮词').fontColor('#E91E63').fontWeight(FontWeight.Bold)
    .onClick(() => {
      this.clickCount++
      this.logText = `第 ${this.clickCount} 次:点了 Span「高亮词」`
    })
  Span(' 看日志变化')
}

Span 支持 .onClick()------点这个片段就触发回调,不影响其他 Span。这是富文本「可点击链接」的基础。

2.3 Span 嵌套 emoji 图标

typescript 复制代码
Text() {
  Span('状态:')
  Span('✓ 在线').fontColor('#4CAF50').fontWeight(FontWeight.Bold)
  Span('  ')
  Span('✗ 离线').fontColor('#9E9E9E')
}

Span 内容支持 emoji------不用单独 Image 组件,直接文本嵌图标。

2.4 Span 动态样式(@State 驱动)

typescript 复制代码
@State highlightWord: string = '鸿蒙'
@State clickCount: number = 0

Text() {
  Span('当前高亮:').fontColor('#666')
  Span(this.highlightWord)
    .fontColor(this.clickCount % 2 === 0 ? '#FF9800' : '#9C27B0')
    .fontSize(18)
    .fontWeight(FontWeight.Bold)
}

Span 样式可绑 @State------this.highlightWord 变了 Span 内容更新,this.clickCount 变了样式色切换。这是「动态高亮词」的基础。


三、真机实拍:Span 五样式 + 动态重绘

我把这个 demo 装到真机上跑(鸿蒙 6.1.1.125, API 24),点底部「切换高亮词」按钮两次,下面两张都是真机实拍,没有任何 P 图。

初始态:① 基础 Span(普通/红粗/蓝斜/下划线/20号大字)+ ② Span 点击事件 + ③ Span emoji + ④ Span 动态样式(高亮「鸿蒙」橙色)+ 日志区:

点切换两次后:日志「第 2 次:高亮词切到「ArkTS」」+ ④ Span 动态样式高亮「ArkTS」紫色:

重点看第二张:日志从「未点击」变成「第 2 次:高亮词切到「ArkTS」」、④ Span 高亮词从「鸿蒙」橙色变成「ArkTS」紫色------Span 内容 + 样式都随 @State 真重绘了。这是 Span 动态样式的真机证明。


四、Span vs 多 Text 拼:啥差异

新手最容易纠结的问题:既然多 Text 拼也能一行多样式,鸿蒙为啥要造 Span?

维度 Text Span 嵌套
行对齐 不齐(行高各自算) 齐(同一行行高)
换行 各自换行错位 一行内不换行
点击事件 分散各 Text 单 Span 单 onClick
性能 多组件重 单 Text 内轻
代码量 多(3 个 Text) 少(1 个 Text + 3 Span)

一句话决策:一行多样式必须用 Span �嵌套,不能用多 Text 拼


五、常见坑(都是血泪)

症状 解法
Span 装在 Text 外 编译报错「Span 必须在 Text 内」 Text() { Span()... } 嵌套
Span 用字符串拼接 样式不生效 Span 是组件不是字符串,用 Text() { Span() }
忘了 .decoration color 下划线颜色错 decoration({type, color}) color 要显式
Span onClick 影响其他 点一个全行触发 Span onClick 单独,不影响其他 Span
动态样式绑 @State 不更新 UI 不重绘 @State 改值才重绘,Span 装在 @Component
emoji 显示方块 字体不支持 鸿蒙默认字体支持 emoji,方块是字体坑换字体

六、Span 适用边界

Span 不是万能------几个场景不适合:

场景 用 Span 吗 推荐组件
一行多样式片段 Span 嵌套
多行段落 多个 Text + Column
图片嵌文本 Text + Image + Row
富文本超链接 Span + onClick
全文相同样式 单 Text 设样式

七、完整代码仓库

本文所有代码都已托管到 AtomGit,欢迎 clone、提 issue、点 star:

🔗 仓库地址atomgit.com/JaneConan/a...

仓库包含:

  • 完整的「Span 四姿势」demo 工程
  • Index.ets 主页面(基础 Span 五样式 + onClick + emoji + 动态样式)
  • 可直接用 DevEco Studio 打开运行(真机装普通应用必能跑)

八、下一步该学什么?

跑通这个 demo 之后,你的鸿蒙富文本就入门了。后续按这个顺序往下:

  1. Image 多源加载(下一篇):网络/本地/资源/沙箱四源
  2. List Section 分组吸顶:大列表分组 + sticky 头
  3. Swiper 自动轮播:轮播 + Indicator + 自动播放
  4. ScrollView 嵌套滚动:Scroll 容器 + 嵌套滚动
  5. Slider 滑块控制:滑块 + onChange + step

写在最后

Span 的本质,是**「鸿蒙给 Text 内嵌富文本片段的原生组件」**------不是多 Text 拼,是单 Text 内多 Span 嵌套。代价是 Text() { } 内嵌多写一层。

一旦你开始用 Span 思维写一行多样式,你会发现大部分「协议页蓝链接」「状态行 emoji 高亮」「日志行加粗关键词」的需求,都是 Span 嵌套的自然结果。代码量比多 Text 拼少一半,对齐性高一倍。

代码已经给你了,仓库链接在上面。现在关掉这篇文章,打开 DevEco Studio,把 demo 跑起来,亲手点切换高亮词感爱下 Span 动态重绘。

跑通了,回来评论区打个「1」,我看看有多少人真的动手了。🚀


作者:JaneConan 仓库:atomgit.com/JaneConan/a... 协议:Apache-2.0,随便用,别告我

相关推荐
卷无止境1 小时前
Python 类型注解与运行时反射:从原理到工程实践
后端·python
耀耀_很无聊1 小时前
13_Spring Boot 3.5.8 + Redisson 3.45.1 导致 Sa-Token 登录 StackOverflowError
spring boot·后端·bootstrap
AskHarries1 小时前
邮件发送方案对比
后端
Ai拆代码的曹操2 小时前
@Async 注解失效的底层真相:this 调用绕过代理,接口响应翻倍
后端
l134062082352 小时前
HarmonyOS应用开发实战:小事记 - 多媒体文件上传:@ohos.net.http 的 multipart/form-data 请求构造
后端·华为·harmonyos·鸿蒙系统
小小猪的春天2 小时前
AI Code Review 例外决策框架:手动忽略警告之前,先回答4个问题
后端·架构
大卫陈2 小时前
微信小程序虚拟支付实战:从「支付能力被限制」到沙箱调通的全过程
前端·后端
武子康2 小时前
vLLM 0.25.1:服务没有报错,为什么仍会生成垃圾 Token(5 级正确性门禁 + 自动回滚条件)
前端·人工智能·后端
Conan在掘金2 小时前
鸿蒙 ArkUI V2 装饰器:@ObservedV2 + @Trace,嵌套对象深层重绘,告别 V1 的「重赋值才更新」
后端