鸿蒙Text文本组件常见问题及解决方案
摘要:本文分析了Text组件的常见问题及解决方案。主要包括:1) 尾部省略号后出现空白的原因及通过设置wordBreak属性解决;2) 行末展开样式的实现方法;3) 不设置maxLines时内容超出显示省略样的解决方案;4) 文本前后添加自定义标签的两种方案。文章提供了详细的代码示例,涵盖单行和多行文本处理、自适应布局策略等技术要点,帮助开发者解决Text组件的常见显示问题。
Text组件尾部省略号后为什么还有一段空白,没有占满组件宽度
问题现象
在Text组件上未设置宽度,当内容过长时,省略号与组件边缘之间会留有较大空白,且内容更新时省略号的位置会发生变化。
原因分析
当Text组件未设置宽度且内容超长时,组件宽度将采用父组件传递的布局约束的最大宽度。省略开始位置会根据不同的断词模式导致排版塑型结果有所不同,因此不同内容的省略开始位置也会不同。
解决措施
设置wordBreak属性为WordBreak.BREAK_ALL,任意2个字符间断行使文本内容尽量占满组件区域。 示例代码如下:
@Entry
@Component
struct Index {
@State message: string = '混合Hello World! honorificabilitudinitatibus!';
build() {
Column() {
Text(this.message)
.id('HelloWorld')
.fontSize('25fp')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis})
.onClick(() => {
this.message = 'Welcome try try try 1235628327434348';
})
.border({ width: 1})
.wordBreak(WordBreak.BREAK_ALL)
}
.width(300)
.border({ width: 1, color: Color.Blue})
.margin({left: 30, top: 50})
}
}
Text组件如何实现行末展开样式
解决措施
自行测算截断字符,并在行末添加…展开或者…图标作为组件内容。
Text组件如何实现不设置maxLines在固定布局约束下内容超出仍显示省略样式
问题现象
在固定尺寸的组件区域内,不同字号的内容显示的最大行数会有所不同。期望实现内容超长时自动显示省略样式,则无需设置固定的maxLines值。
解决措施
设置heightAdaptivePolicy为TextHeightAdaptivePolicy.LAYOUT_CONSTRAINT_FIRST,该模式会删除超过布局约束的行,从而实现类似设置maxLines的效果。 示例代码如下:
@Entry
@Component
struct Index {
@State message: string = '混合Hello World! 多行文本 中英文数字混合 1282378283 ~';
@State fontSize: number = 25;
build() {
Column({ space: 10 }) {
Text(this.message)
.id('HelloWorld')
.fontSize(this.fontSize)
.textOverflow({ overflow: TextOverflow.Ellipsis})
.border({ width: 1})
.heightAdaptivePolicy(TextHeightAdaptivePolicy.LAYOUT_CONSTRAINT_FIRST) // 调整自适应布局策略
.width(300)
.height(200)
Row(){
Button('fontSize+5')
.onClick(()=>{
this.fontSize += 5;
})
Button('fontSize-5')
.onClick(()=>{
this.fontSize -= 5;
})
}
}
.margin({left: 30, top: 50})
}
}

在文本前后添加自定义标签
问题现象
如何在文本的前后各添加一个标签,例如“专题”或“Top1”。这些标签的背景样式、尺寸设置需要能够自定义。
解决措施一
如果标签和中间的长文本需在同一行显示,开发者可能会考虑使用Span实现,但是Span不支持设置尺寸。此时,可以在弹性布局 (Flex)或者Row中放置标签和长文本,并为长文本设置textOverflow属性,以确保文本超长时能够自适应截断,显示在一行之内。 实现步骤:
- 将标签和长文本放在同一个沿水平方向布局的容器Row中。
- 中间长文本设置textOverflow属性为TextOverflow.Ellipsis,空间不足时截断文本,显示省略号。
实现案例可以参考实现热搜榜,该示例中,文字“1”、“爆”就是“我是热搜词条”的两个标签。这种实现方式写法简便,适合单行文本添加标签的场景。
解决措施二
如果需求是在多行文本前后添加标签,并且不截断文本,上面的方案会导致三个Text中的文本不能对齐。此时,可以在层叠布局 (Stack)中放置标签和长文本,给中间多行文本设置首行文本缩进距离textIndent。多行文本后面的标签则需要通过offset属性调整位置。这种实现方式,可以让三个Text组件中的文字水平对齐。 实现步骤:
- 将标签和长文本放在Stack中。
- 在显示之前的回调aboutToAppear中,使用measureTextSize计算前标签的宽度,作为中间多行文本的首行缩进距离。
- 通过getparagraphs计算中间多行文本最后一行的宽度、除最后一行文本之外的高度,作为后标签的偏移量offset。
- 设置后标签相对于Stack左上角的偏移量。
import { LengthMetrics } from '@kit.ArkUI';
@Entry
@Component
struct Index {
@State message: string = '这是一段长文本,超长部分折行,前后添加标签';
@State frontTag: string = '前标签';
@State backTag: string = '后标签';
@State frontPaddingVp: number = 20;
@State backPaddingVp: number = 10;
@State fontTagWidthVp: Length = 0;
@State backTagWidthVp: Length = 0;
@State backOffsetVpX: Length = 0;
@State backOffsetVpY: Length = 0;
@State messageLines: number = 0;
@State stackWidthVp: number = 300;
// 显示之前,测算前后标签的位置,中间文本的缩进距离
aboutToAppear(): void {
// 计算前标签的宽度fontTagWidthVp,作为message的首行缩进距离
let frontTagSize: SizeOptions = this.getUIContext().getMeasureUtils().measureTextSize({
textContent: this.frontTag,
});
this.fontTagWidthVp = this.getUIContext().px2vp(Number(frontTagSize.width)) + this.frontPaddingVp * 2
// 计算frontTag+message占据的行数
let linesFrontTagPlusMessage = 0;
let mutableStr = new MutableStyledString(this.message,
[{
start: 0,
length: 1,
styledKey: StyledStringKey.PARAGRAPH_STYLE,
styledValue: new ParagraphStyle({ textIndent: LengthMetrics.vp(this.fontTagWidthVp) })
}]
)
let paragraphArr = this.getUIContext()
.getMeasureUtils()
.getParagraphs(mutableStr, { constraintWidth: LengthMetrics.vp(this.stackWidthVp) });
for (let i = 0; i < paragraphArr.length; ++i) {
linesFrontTagPlusMessage += paragraphArr[i].getLineCount();
}
// 后标签offsetX的偏移量backOffsetVpX=frontTag+message最后一行的宽度
this.backOffsetVpX =
this.getUIContext().px2vp((paragraphArr[paragraphArr.length-1].getLineWidth(linesFrontTagPlusMessage - 1)))
// 后标签offsetY的偏移量backOffsetVpY=frontTag+message总高度-最后一行的高度。
let heightFrontTagPlusMessageVp = 0;
for (let i = 0; i < paragraphArr.length; ++i) {
heightFrontTagPlusMessageVp += this.getUIContext().px2vp(paragraphArr[i].getHeight());
}
let lastLineHeight =
this.getUIContext().px2vp(paragraphArr[paragraphArr.length-1].getLineHeight(linesFrontTagPlusMessage - 1))
this.backOffsetVpY = heightFrontTagPlusMessageVp - lastLineHeight
}
build() {
Column({ space: 20 }) {
Blank()
.height(200)
Stack() {
Text(this.frontTag)
.padding({ left: this.frontPaddingVp, right: this.frontPaddingVp })
.backgroundColor('rgb(39, 135, 217)')
Text(this.message)
.textIndent(this.fontTagWidthVp)
.padding(0)
Text(this.backTag)
.padding({ left: this.backPaddingVp, right: this.backPaddingVp })
.backgroundColor('rgb(0, 74, 175)')
.offset({
x: this.backOffsetVpX,
y: this.backOffsetVpY
})
}
.alignContent(Alignment.TopStart) // 顶部起始端对齐
.width(this.stackWidthVp)
}
.height('100%')
.width('90%')
.padding('5%')
}
}

Text组件如何实现表情与文字一起显示
问题现象
emoji表情有时以表情符号的形式表示。如何将表情符号转换为emoji表情,并在Text组件中与文字一同显示?
解决措施
使用正则表达式解析表情符号,再将表情符号与图片资源建立映射,通过Span和ImageSpan来同时展示表情和文字。
@Entry
@Component
struct Index {
@State fulltext: string =
'你好我是Text[grin],你好我[rolling_on_the_floor_laughing]是Text,[slightly_smiling_face]你好我是Text[grin]';
static classifyTextAndEmojis(input: string): Map<string, string[]> {
const emojiRegex = /\[([a-zA-Z_]+)\]/g; // 根据实际情况编写正则表达式
const resultMap = new Map<string, string[]>(); // 用map记录普通文本和表情
resultMap.set('text', []);
resultMap.set('emojis', []);
let lastIndex = 0;
let match: RegExpExecArray | null = null;
while ((match = emojiRegex.exec(input)) !== null) {
// 添加普通文本
if (match.index > lastIndex) {
resultMap.get('text')?.push(input.substring(lastIndex, match.index));
}
// 添加匹配到的表情
resultMap.get('emojis')?.push(match[1]);
lastIndex = match.index + match[0].length;
}
// 添加最后一段文本
if (lastIndex < input.length) {
resultMap.get('text')?.push(input.substring(lastIndex));
}
return resultMap;
}
static getEmojiImg(emojis: string[]): Resource[] { // 根据正则匹配结果返回自定义表情资源
let emojisImg: Resource[] = []
for (let i = 0; i < emojis.length; i++) {
switch (emojis[i]) { // $r("...")需要替换为开发者所需的图像资源
case 'rolling_on_the_floor_laughing':
emojisImg.push($r("app.media.rolling_on_the_floor_laughing"))
case 'slightly_smiling_face':
emojisImg.push($r("app.media.slightly_smiling_face"))
case 'grin':
emojisImg.push($r("app.media.grin"))
default:
}
}
return emojisImg
}
build() {
Column() {
TextInput({
placeholder: "用户输入带表情的文本,例如:你好[grin]"
})
.width('80%')
.padding(10)
.border({ width: 1, color: '#EEEEEE' })
.onChange((value: string) => {
// 输入变化时,更新 fulltext
this.fulltext = value;
});
Text() {
ForEach(TextExample.classifyTextAndEmojis(this.fulltext).get('text'),
(item: string, index: number) => { // 展示文本和自定义表情资源
Span(item)
.fontSize(18)
.fontColor('#666666')
.fontWeight(FontWeight.Regular)
ImageSpan(TextExample.getEmojiImg(
TextExample.classifyTextAndEmojis(this.fulltext).get('emojis'))[index])
.verticalAlign(ImageSpanAlignment.BOTTOM)
.height(24)
})
}
.width('80%')
.padding(15)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.padding(20)
}
}

文本超长时如何展示
问题现象
Text组件中内容太多,超出父组件容器Column的高度,导致显示混乱。如何让文本显示在父组件容器的区域内。
解决措施一
Text文本是自动折行的,当没有限制Text高度height时,Text高度在文本的行数增加时自动调整。可以通过设置maxLines属性限制文本的最大行数,如果有多余的文本默认会被截断。也可以通过textOverflow属性来指定截断方式。 以下示例展示了限制Text组件不超过三行的场景。
@Entry
@Component
struct Index {
@State message: string = '这是一段超长文本'.repeat(50);
build() {
Column() {
Text(this.message)
.height('auto')
.maxLines(3)
}
.height(200)
.width('80%')
.margin('10%')
.borderWidth(1)
.justifyContent(FlexAlign.Center)
}
}

解决措施二
解决措施一的缺点是有部分文本被裁剪掉,如果开发者想要全部文本可以被阅读,可以把Text组件放在滚动容器Scroll里面,通过手势滑动来浏览全部文本。
@Entry
@Component
struct Index {
@State message: string = '这是一段超长文本'.repeat(50);
build() {
Column() {
Scroll() {
Text(this.message)
}
.scrollBar(BarState.Off)
}
.height(200)
.width('80%')
.margin('10%')
.borderWidth(1)
.justifyContent(FlexAlign.Center)
}
}

更多推荐



所有评论(0)