【Harmony Next 应用开发入门】第七期:一文搞定文本显示 (Text/Span)组件
·
好久没写博客啦,延续前几期的风格,小鱼用一个“带便利贴功能的记事本”来比喻Text/Span两个组件。
比喻:带便利贴的记事本
想象你要写一份文档:
- Text组件 = 一页完整的记事本纸张
- Span组件 = 贴在纸上的彩色便利贴
一、 Text 组件:记事本纸张
- 基本文本与资源引用:
Text('...')、Text($r('app.string.xxx')) - 视觉样式:
- 字体:
fontSize、fontColor、fontWeight、fontFamily - 盒模型:
padding、margin、width、height - 边框:
border、borderWidth、borderRadius
- 字体:
示例:
Text('我是一段文本')
Text($r('app.string.text1'))
Text($r('app.string.text1'))
.baselineOffset(0)
.fontSize(30)
.border({ width: 1 })
.borderRadius(10)
.padding(10)
.width(300)
二、Span 组件:贴在纸上的彩色便利贴
Text(){ Span() }组合富文本- Span 独立样式:字号、颜色、斜体、装饰线
- Span 点击事件示例:点击触发状态更新
Text($r('app.string.text2')) {
Span($r('app.string.text2span'))
}
.padding(10)
.borderWidth(1)
Text() {
Span($r('app.string.text3span1'))
.fontSize(16)
.fontColor(Color.Grey)
.decoration({ type: TextDecorationType.LineThrough, color: Color.Red })
Span($r('app.string.text3span2'))
.fontColor(Color.Blue)
.fontSize(16)
.fontStyle(FontStyle.Italic)
.decoration({ type: TextDecorationType.Underline, color: Color.Black })
Span($r('app.string.text3span3'))
.fontSize(16)
.fontColor(Color.Grey)
.decoration({ type: TextDecorationType.Overline, color: Color.Green })
}
.borderWidth(1)
.padding(10)
Text() {
Span('I am Upper-span')
.textCase(TextCase.UpperCase)
.fontSize(30)
.onClick(() => {
this.textStr1 = 'Span onClick is triggering';
})
}
Text('onClick:' + this.textStr1)
.fontSize(20)
三、对齐与排版控制
- 水平对齐:
textAlign(Start/Center/End) - 垂直对齐:
textVerticalAlign - 基线偏移:
baselineOffset - 字符间距:
letterSpacing - 行高:
lineHeight - 行间距(API 20):
lineSpacing(LengthMetrics.px(...))
Text('左对齐')
.width(300)
.textAlign(TextAlign.Start)
.border({ width: 1 })
.padding(10)
Text('中间对齐')
.width(300)
.textAlign(TextAlign.Center)
.border({ width: 1 })
.padding(10)
Text('右对齐')
.width(300)
.textAlign(TextAlign.End)
.border({ width: 1 })
.padding(10)
Text('This is the text with the line height set. This is the text with the line height set.')
.width(300).fontSize(12).border({ width: 1 }).padding(10)
Text('This is the text with the line height set. This is the text with the line height set.')
.width(300)
.fontSize(12)
.border({ width: 1 })
.padding(10)
.lineHeight(20)
Text('decoration属性设置文本装饰线样式、颜色及其粗细').fontSize(20).fontColor(Color.Green).fontWeight(700)
Text('This is the text:LineThrough')
.decoration({
type: TextDecorationType.LineThrough,
color: Color.Red
})
.borderWidth(1).padding(15).margin(5)
Text('This is the text:Overline')
.decoration({
type: TextDecorationType.Overline,
color: Color.Red
})
.borderWidth(1).padding(15).margin(5)
Text('This is the text:Underline')
.decoration({
type: TextDecorationType.Underline,
color: Color.Red
})
.borderWidth(1).padding(15).margin(5)
Text('This is the text:Underline/DASHED')
.decoration({
type: TextDecorationType.Underline,
color: Color.Blue,
style: TextDecorationStyle.DASHED
})
.borderWidth(1).padding(15).margin(5)
Text('This is the text:Underline/DOTTED')
.decoration({
type: TextDecorationType.Underline,
color: Color.Blue,
style: TextDecorationStyle.DOTTED
})
.borderWidth(1).padding(15).margin(5)
Text('This is the text:Underline/DOUBLE')
.decoration({
type: TextDecorationType.Underline,
color: Color.Blue,
style: TextDecorationStyle.DOUBLE
})
.borderWidth(1).padding(15).margin(5)
Text('This is the text:Underline/WAVY')
.decoration({
type: TextDecorationType.Underline,
color: Color.Blue,
style: TextDecorationStyle.WAVY,
thicknessScale: 4
})
.borderWidth(1).padding(15).margin(5)
Text('baselineOffset属性设置文本基线的偏移量').fontSize(20).fontColor(Color.Green).fontWeight(700)
Text('This is the text content with baselineOffset 0.')
.baselineOffset(0)
.fontSize(12)
.border({ width: 1 })
.padding(10)
.width('100%')
.margin(5)
Text('This is the text content with baselineOffset 30.')
.baselineOffset(30)
.fontSize(12)
.border({ width: 1 })
.padding(10)
.width('100%')
.margin(5)
Text('This is the text content with baselineOffset -20.')
.baselineOffset(-20)
.fontSize(12)
.border({ width: 1 })
.padding(10)
.width('100%')
.margin(5)
Text('letterSpacing属性设置文本字符间距').fontSize(20).fontColor(Color.Green).fontWeight(700)
Text('This is the text content with letterSpacing 0.')
.letterSpacing(0)
.fontSize(12)
.border({ width: 1 })
.padding(10)
.width('100%')
.margin(5)
Text('This is the text content with letterSpacing 3.')
.letterSpacing(3)
.fontSize(12)
.border({ width: 1 })
.padding(10)
.width('100%')
.margin(5)
Text('This is the text content with letterSpacing -1.')
.letterSpacing(-1)
.fontSize(12)
.border({ width: 1 })
.padding(10)
.width('100%')
.margin(5)
四、溢出与省略策略
- 溢出模式:
TextOverflow.NoneTextOverflow.EllipsisTextOverflow.MARQUEE
- 跑马灯配置:
marqueeOptions - 混合中英数字截断:
wordBreak(WordBreak.BREAK_ALL)+ellipsisMode
Text('textOverflow 属性的使用').fontSize(20).fontColor(Color.Green).fontWeight(700)
Text('This is the setting of textOverflow to Clip text content This is the setting of textOverflow ' +
'to None text content. This is the setting of textOverflow to Clip text content This is the setting ' +
'of textOverflow to None text content.')
.width(250)
.textOverflow({ overflow: TextOverflow.None })
.maxLines(1)
.fontSize(12)
.border({ width: 1 })
.padding(10)
Text('我是超长文本,超出的部分显示省略号 I am an extra long text, with ellipses displayed for any excess。')
.width(250)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.maxLines(1)
.fontSize(12)
.border({ width: 1 })
.padding(10)
Text('当文本溢出其尺寸时,文本将滚动显示 When the text overflows its dimensions,the text will scroll for displaying.')
.width(250)
.textOverflow({ overflow: TextOverflow.MARQUEE })
.maxLines(1)
.fontSize(12)
.border({ width: 1 })
.padding(10)
Text('API 18 属性')
Text('当文本溢出其尺寸时,文本将滚动显示,支持设置跑马灯配置项 When the text overflows its dimensions, the text will scroll for displaying.')
.width(250)
.textOverflow({ overflow: TextOverflow.MARQUEE })
.maxLines(1)
.fontSize(12)
.border({ width: 1 })
.padding(10)
.marqueeOptions({
start: true,
fromStart: true,
step: 6,
loop: -1,
delay: 0,
fadeout: false,
marqueeStartPolicy: MarqueeStartPolicy.DEFAULT
})
五、字体自适应与大小写
- 自适应字号:
- 必须同时设置
minFontSize与maxFontSize - 配合
maxLines或容器尺寸
- 必须同时设置
- 大小写控制:
textCase(Normal/LowerCase/UpperCase)
//minFontSize用于设置文本的最小显示字号,maxFontSize用于设置文本的最大显示字号。这两个属性必须同时设置才能生效,并且需要与maxLines属性或布局大小限制配合使用,单独设置任一属性将不会产生效果
Text('我的最大字号为30,最小字号为5,宽度为250,maxLines为1')
.width(250)
.maxLines(1)
.maxFontSize(30)
.minFontSize(5)
.border({ width: 1 })
.padding(10)
.margin(5)
Text('我的最大字号为30,最小字号为5,宽度为250,maxLines为2')
.width(250)
.maxLines(2)
.maxFontSize(30)
.minFontSize(5)
.border({ width: 1 })
.padding(10)
.margin(5)
Text('我的最大字号为30,最小字号为15,宽度为250,高度为50')
.width(250)
.height(50)
.maxFontSize(30)
.minFontSize(15)
.border({ width: 1 })
.padding(10)
.margin(5)
Text('我的最大字号为30,最小字号为15,宽度为250,高度为100')
.width(250)
.height(100)
.maxFontSize(30)
.minFontSize(15)
.border({ width: 1 })
.padding(10)
.margin(5)
Text('textCase属性设置文本大小写').fontSize(20).fontColor(Color.Green).fontWeight(700)
Text('This is the text content with textCase set to Normal.')
.textCase(TextCase.Normal)
.padding(10)
.border({ width: 1 })
.padding(10)
.margin(5)
// 文本全小写展示
Text('This is the text content with textCase set to LowerCase.')
.textCase(TextCase.LowerCase)
.border({ width: 1 })
.padding(10)
.margin(5)
// 文本全大写展示
Text('This is the text content with textCase set to UpperCase.')
.textCase(TextCase.UpperCase)
.border({ width: 1 })
.padding(10)
.margin(5)
六、复制与交互
- 文本可复制:
copyOption(CopyOptions.InApp)
Text('copyOption属性设置文本是否可复制粘贴').fontSize(20).fontColor(Color.Green).fontWeight(700)
Text('这是一段可复制文本。')
.fontSize(30)
.copyOption(CopyOptions.InApp)
Text('fontFamily属性设置字体列表。应用当前支持\'HarmonyOS Sans\'字体和注册自定义字体。')
.fontSize(20)
.fontColor(Color.Green)
.fontWeight(700)
Text('This is the text content with fontFamily')
.fontSize(30)
.fontFamily('HarmonyOS Sans')
七、API 20 特性汇总
- 数字翻牌:
contentTransition(NumericTextTransition) - 行尾空格优化:
optimizeTrailingSpace(true/false) - 中西文自动间距:
enableAutoSpacing(...) - 渐变文字:
shaderStyle(linearGradientOptions) - 行间距:
lineSpacing
//API version 20开始,支持通过contentTransition属性设置数字翻牌效果
Text('API 20 contentTransition属性设置数字翻牌效果').fontSize(20).fontColor(Color.Green).fontWeight(700)
Text(this.number + '')
.borderWidth(1)
.fontSize(40)
.contentTransition(this.numberTransition)
Button('chang number')
.onClick(() => {
this.number++
})
.margin(10)
// 支持通过optimizeTrailingSpace设置是否在文本布局过程中优化每行末尾的空格,可解决行尾空格影响对齐显示效果问题
Text('API 20 通过optimizeTrailingSpace设置是否在文本布局过程中优化每行末尾的空格,可解决行尾空格影响对齐显示效果问题')
.fontSize(20)
.fontColor(Color.Green)
.fontWeight(700)
// 启用优化行尾空格功能
Text('Trimmed space enabled ')
.fontSize(30)
.fontWeight(FontWeight.Bold)
.margin({ top: 20 })
.optimizeTrailingSpace(true)
.textAlign(TextAlign.Center)
// 不启用优化行尾空格功能
Text('Trimmed space disabled ')
.fontSize(30)
.fontWeight(FontWeight.Bold)
.margin({ top: 20 })
.optimizeTrailingSpace(false)
.textAlign(TextAlign.Center)
Text('API 20,支持通过lineSpacing设置文本的行间距').fontSize(20).fontColor(Color.Green).fontWeight(700)
Text('The line spacing of this context is set to 20_px, and the spacing is effective only between the lines.')
.lineSpacing(LengthMetrics.px(20), { onlyBetweenLines: true })
.width(250)
.height(100)
.maxFontSize(30)
.minFontSize(15)
.border({ width: 1 })
Text('API 20,enableAutoSpacing设置是否开启中文与西文的自动间距')
.fontSize(20)
.fontColor(Color.Green)
.fontWeight(700)
Text(this.enableSpacing ? '已开启自动间距' : '当前状态:已关闭自动间距')
.fontSize(16)
.fontColor(this.enableSpacing ? '#4CAF50' : '#F44336')
.margin({ bottom: 20 })
// 设置是否应用中西文自动间距
Text('中西文Auto Spacing自动间距')
.fontSize(24)
.padding(15)
.backgroundColor('#F5F5F5')
.width('90%')
.enableAutoSpacing(this.enableSpacing)
.onClick(() => {
this.enableSpacing = !this.enableSpacing;
})
八、渐变文字实现方式
- API 20 前:
Column + linearGradient + blendMode实现裁切渐变 - API 20 起:直接
shaderStyle
Text('API 20 前, shaderStyle设置渐变色').fontSize(20).fontColor(Color.Green).fontWeight(700)
Column() {
Text(`API 20 前, shaderStyle设置渐变色`)
.fontSize(24)
.fontWeight(900)
.blendMode(BlendMode.DST_IN, BlendApplyType.OFFSCREEN) // 关键:裁切背景
}
.linearGradient({
angle: 0,
colors: [['#F8E74F', 0], ['#67E447', 1]]
})
.blendMode(BlendMode.SRC_OVER, BlendApplyType.OFFSCREEN)
//PI version 20开始,支持通过shaderStyle设置渐变色
Text('API 20, shaderStyle设置渐变色').fontSize(20).fontColor(Color.Green).fontWeight(700)
Text('API 20, shaderStyle设置渐变色')
.fontSize(24)
.fontWeight(900)
.width('80%')
.shaderStyle(this.linearGradientOptions)
九、图文混排
ImageSpan与Span混排ImageSpanAlignment.FOLLOW_PARAGRAPH对齐基线textVerticalAlign控制段落垂直对齐
Text('API 20,textVerticalAlign属性实现文本段落在垂直方向的对齐').fontSize(20).fontColor(Color.Green).fontWeight(700)
Text() {
Span('听原音')
.fontSize(50)
ImageSpan($r('app.media.ic_dubbing_listen'))
.width(30).height(30)
.verticalAlign(ImageSpanAlignment.FOLLOW_PARAGRAPH)
Span('去配音')
}
.padding(10)
.textVerticalAlign(TextVerticalAlign.CENTER)
.borderWidth(1).borderRadius(12).borderColor(Color.Blue)
Text('这是一段文本,用来展示选中菜单')
.fontSize(30)
.copyOption(CopyOptions.InApp)
Text() {
Span('今天是') // 普通文字
Span('2024年12月24日') // 第一张便利贴(红色)
.fontColor(Color.Red)
.fontWeight(FontWeight.Bold)
Span(',天气') // 又回到普通文字
Span('晴朗') // 第二张便利贴(蓝色+下划线)
.fontColor(Color.Blue)
.decoration({ type: TextDecorationType.Underline })
Span('。') // 结束
}
十、实战示例
1. 自适应字号 + 省略
- 输入框驱动文本变化
- 使用
maxFontSize/minFontSize+ellipsisMode+maxLines
Column({ space: 30 }) {
TextInput({ text: this.example1 })
.onChange((value: string) => {
// 文本内容发生变化时触发该回调
this.example1 = value;
})
Row() {
Text('大')
.width(40)
.height(40)
.backgroundColor(Color.Orange)
.fontSize(35)
Text(this.example1)
.maxFontSize(30)
.minFontSize(15)
.constraintSize({
minWidth: 20,
maxWidth: '100%'
})
.ellipsisMode(EllipsisMode.END)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.maxLines(1)
.textAlign(TextAlign.Center)
.fontColor(Color.Black)
.fontWeight(500)
.layoutWeight(1)
.height(40)
Text('小')
.width(40)
.height(40)
.backgroundColor(Color.Orange)
.fontSize(15).textAlign(TextAlign.Center)
}
.width('100%')
.height('auto')
}
.height('auto')
.width('100%')
2. 混排文本截断异常
- 采用
WordBreak.BREAK_ALL逐字截断
Text('实战2:如何解决Text组件文本为中文、数字、英文混合时显示省略号截断异常的问题').fontSize(20).fontColor(Color.Green).fontWeight(700)
Text('2 years · VIP membership for 3 months · 8GB · 230mm · Product color\u200B')
.width(200)// Set maximum number of rows
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })// Long text display ellipsis
.ellipsisMode(EllipsisMode.END)// Set the line breaking rule WordBreak.BREAK_ALL and implement truncation on a letter by letter basis
.wordBreak(WordBreak.BREAK_ALL)// API11+ required, for letter-level truncation
.textAlign(TextAlign.JUSTIFY)
.backgroundColor(Color.Green)
.fontSize(16)
3. 热搜列表布局
Column + Row组合- 序号、标签、热度标识统一布局与约束宽度
Column() {
Row() {
Text('1').fontSize(14).fontColor(Color.Red).margin({ left: 10, right: 10 })
Text('我是热搜词条1')
.fontSize(12)
.fontColor(Color.Blue)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.fontWeight(300)
Text('爆')
.margin({ left: 6 })
.textAlign(TextAlign.Center)
.fontSize(10)
.fontColor(Color.White)
.fontWeight(600)
.backgroundColor(0x770100)
.borderRadius(5)
.width(15)
.height(14)
}.width('100%').margin(5)
Row() {
Text('2').fontSize(14).fontColor(Color.Red).margin({ left: 10, right: 10 })
Text('我是热搜词条2 我是热搜词条2 我是热搜词条2 我是热搜词条2 我是热搜词条2')
.fontSize(12)
.fontColor(Color.Blue)
.fontWeight(300)
.constraintSize({ maxWidth: 200 })
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text('热')
.margin({ left: 6 })
.textAlign(TextAlign.Center)
.fontSize(10)
.fontColor(Color.White)
.fontWeight(600)
.backgroundColor(0xCC5500)
.borderRadius(5)
.width(15)
.height(14)
}.width('100%').margin(5)
Row() {
Text('3').fontSize(14).fontColor(Color.Orange).margin({ left: 10, right: 10 })
Text('我是热搜词条3')
.fontSize(12)
.fontColor(Color.Blue)
.fontWeight(300)
.maxLines(1)
.constraintSize({ maxWidth: 200 })
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text('热')
.margin({ left: 6 })
.textAlign(TextAlign.Center)
.fontSize(10)
.fontColor(Color.White)
.fontWeight(600)
.backgroundColor(0xCC5500)
.borderRadius(5)
.width(15)
.height(14)
}.width('100%').margin(5)
Row() {
Text('4').fontSize(14).fontColor(Color.Grey).margin({ left: 10, right: 10 })
Text('我是热搜词条4 我是热搜词条4 我是热搜词条4 我是热搜词条4 我是热搜词条4')
.fontSize(12)
.fontColor(Color.Blue)
.fontWeight(300)
.constraintSize({ maxWidth: 200 })
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}.width('100%').margin(5)
}.width('100%').height('auto') .padding({ left: 12, right: 12 })
一句话总结
Text 和 Span 就像“记事纸和便利贴”:
- Text = 完整的纸张,展示大段文字 + 支持选中自定义菜单
- Span = 彩色便利贴,装饰局部文字(改颜色、加粗、变样式)
- 关系:便利贴(Span)必须贴在纸(Text)上才能用
- 特色功能:Text可以让你自定义选中文字后的菜单(复制/翻译/分享等)
最后上图,如需要源码,请私信。

更多推荐



所有评论(0)