鸿蒙 Next Text 长文本实现展开收起 2 种模式
作者:auhgnixgnahz
- 2025-06-23 北京
本文字数:2954 字
阅读完需:约 10 分钟
当展示长文本时,通过会设置 maxLines(value: number),则文本最多不会超过指定的行。如果有多余的文本,可以通过 textOverflow 来指定截断方式。单独设置 textOverflow 不生效。设置 TextOverflow.None 与 TextOverflow.Clip 效果相同。
本文介绍 2 种样式的展开、收起样式:这里以 2 行为例
1.仿朋友圈样式,在内容文字下一行增加展开收起,这种实现比较简单,直接在文字下一行增加一个 Text,通过设置 maxLines 的值即可实现,如果想实现当大于指定行数才显示展开,可参考第二种方式计算文字行数。
2.仿头条新闻标题展开、收起样式,文本的末尾是…展开,由于直接设置 TextOverflow.Ellipsis 展开不能和第一行文字对齐。因此我们需要动态计算一下**(实际展示的文字+…展开)正好占两行时可展示多少个文字,这样就可以实现…展开展示到第二行的末尾。动态计算文本高度,需要用到 MeasureText.measureTextSize**代码种有注释,可以参考
实现效果:

实现代码:
import { LengthUnit, MeasureText } from '@kit.ArkUI';
import { getScreenWidth } from '../utils/DisplayUtil';
@Entry
@ComponentV2
struct textExpand{
allContent:string = '道可道,非常道;名可名,非常名。无名,天地之始;有名,万物之母。故常无,欲以观其妙;常有,欲以观其徼。此两者,同出而异名,同谓之玄。玄之又玄,众妙之门。'
@Local title:string= this.allContent;
@Local title2:string=this.allContent;
@Local titleMaxLine:number=2;
@Local isExpand:boolean = false;
@Local isExpand2:boolean = false;
@Local textWidth:number = getScreenWidth()-20
textFontSize:number = 18
aboutToAppear(): void {
this.collapseText()
}
build() {
Column({space:5}){
Text('仿微信朋友圈文本底部展开收起样式').fontSize(20).fontColor(Color.Red)
Text(this.title).fontSize(this.textFontSize).fontColor(Color.Black)
.textOverflow({overflow:TextOverflow.Ellipsis})
.maxLines(this.titleMaxLine)
.lineSpacing({value:5,unit:LengthUnit.VP}) //设置文本的行间距
Text(this.isExpand?'收起':'展开').fontSize(this.textFontSize).fontColor(Color.Blue)
.onClick(()=>{
if (this.isExpand) {
this.titleMaxLine=2;
this.isExpand=false;
}else {
this.titleMaxLine=-1;
this.isExpand=true;
}
})
Text('仿微头条文本末尾展开收起样式').fontSize(20).fontColor(Color.Red)
Text('文本组件宽:'+this.textWidth)
Text(){
Span(this.title2).fontColor(Color.Black)
Span(this.isExpand2?'收起':'展开').fontColor(Color.Blue)
.onClick(()=>{
if (this.isExpand2) {
this.collapseText()
}else {
this.expandText()
}
})
}
.fontSize(this.textFontSize)
.onSizeChange( (oldValue: SizeOptions, newValue: SizeOptions)=>{
this.textWidth = newValue.width as number
})
}.alignItems(HorizontalAlign.Start)
.padding({left:10,right:10})
}
expandText(): void {
this.isExpand2 = true;
this.title2 = this.allContent;
}
//收起文本
collapseText(): void {
// 展开文本的size
let expandSize: SizeOptions = MeasureText.measureTextSize({
textContent: this.title2, //设置被计算文本内容
constraintWidth: this.textWidth,//设置被计算文本布局宽度
fontSize: this.textFontSize //设置被计算文本字体大小
});
// 将要收起的文本size
let collapseSize: SizeOptions = MeasureText.measureTextSize({
textContent: this.title2, //设置被计算文本内容
constraintWidth: this.textWidth,//设置被计算文本布局宽度
fontSize: this.textFontSize, //设置被计算文本字体大小
maxLines: this.titleMaxLine //设置被计算文本最大行数
});
//收起的文本高度和展开时的文本高度相等时,不需要展示收起和展开,即不需要处理
if ((expandSize.height as number) == (collapseSize.height as number)) {
this.isExpand2 = false;
return;
}
let clipTitle: string = this.title2
//因为设置Ellipsis之后,省略号显示到文本末尾 想要在省略号后面加展开,
// 可以通过截断文字,然后再拼接...展开展示到文本末尾
//因此,需要计算展示多少个文字拼接上(...展开) 刚好展示到文本末尾
//这里介绍2种方法
//1.逐个遍历计算文字长度,找到最多可以展示多少个字符
let indexCursor = 0;
while (true){
let tempTitle = this.title2.substring(0, indexCursor) + '...展开';
const currentLinesTextSize: SizeOptions = this.calcTextSize(tempTitle)
//从第一个字符开始计算,直到.....展开内容高度刚好大于第二行时,截取上一个字符即 能展示的所有文字
if ((currentLinesTextSize.height as number) > (collapseSize.height as number)) {
break
}else {
indexCursor++
}
}
clipTitle = this.title2.substring(0, indexCursor - 1);
this.title2 = clipTitle + '...';
//2. 使用二分法查找正好两行的长度的字符串
// let leftCursor: number = 0;
// let rightCursor: number = this.title2.length;
// let cursor: number = Math.floor(rightCursor / 2);
// let tempTitle: string = '';
// while (true) {
// tempTitle = this.title2.substring(0, cursor) + '...展开';
// const currentLinesTextSize: SizeOptions = this.calTextSize(tempTitle)
//
// if ((currentLinesTextSize.height as number) > (collapseSize.height as number)) {
// // 当前字符已超过两行,向左继续找
// rightCursor = cursor;
// cursor = leftCursor + Math.floor((cursor - leftCursor) / 2);
// } else {
// // 当前字符小于两行了,可能已经ok,但仍需向右查找
// leftCursor = cursor;
// cursor += Math.floor((rightCursor - cursor) / 2);
// }
// if (Math.abs(rightCursor - leftCursor) <= 1) {
// // 两次指针基本重合了,代表已找到
// break;
// }
// }
// clipTitle = this.title2.substring(0, cursor - 1);
// this.title2 = clipTitle + '...';
this.isExpand2 = false;
}
calcTextSize(text:string):SizeOptions{
return MeasureText.measureTextSize({
textContent: text,
fontSize: this.textFontSize,
wordBreak: WordBreak.BREAK_ALL, //设置断行规则
constraintWidth: this.textWidth
});
}
}
复制代码
划线
评论
复制
发布于: 刚刚阅读数: 3
版权声明: 本文为 InfoQ 作者【auhgnixgnahz】的原创文章。
原文链接:【http://xie.infoq.cn/article/99bd8d770d3fadc8687b4beff】。文章转载请联系作者。

auhgnixgnahz
关注
还未添加个人签名 2018-07-10 加入
还未添加个人简介
评论