鸿蒙Next Text长文本实现展开收起2种模式

2025-06-27 22:48:06
105次阅读
0个评论

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

实现效果:

演示.gif

实现代码:

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
    });
  }
}
收藏00

登录 后评论。没有帐号? 注册 一个。