鸿蒙中 文本的展开与折叠实现

本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

鸿蒙(HarmonyOS)开发中,实现文本的展开与折叠功能是常见的交互,尤其在新闻、评论等场景中。以下是详细的实现方案,涵盖 基础实现、动画效果、 及 代码示例

一、基础实现:文本截断与展开

1. 核心API与属性
API/属性作用类型
maxLines控制文本最大行数number
textOverflow超出部分显示方式(如ellipsis{overflow: TextOverflow}
onClick点击事件绑定() => void
2. 基础实现代码
@Entry
@Component
struct ExpandableText {
  @State isExpanded: boolean = false; // 控制展开状态
  @State fullText: string = '这是一段需要展开的长文本...'.repeat(10); // 长文本

  build() {
    Column() {
      // 文本部分:根据状态切换行数
      Text(this.fullText)
        .maxLines(this.isExpanded ? undefined : 3) // 折叠时显示3行
        .textOverflow({ overflow: TextOverflow.Ellipsis }) // 超出显示省略号
        .fontSize(16)
        .margin(10)

      // 展开/折叠按钮
      Button(this.isExpanded ? '收起' : '展开全文')
        .onClick(() => {
          this.isExpanded = !this.isExpanded; // 切换状态
        })
        .margin({ top: 5 })
    }
  }
}

二、高级功能扩展

1. 动画效果(平滑展开)

使用 animateTo 实现过渡动画:

import { animateTo } from '@ohos.animator';

@State textHeight: number = 60; // 初始高度(3行文本高度)

build() {
  Column() {
    Text(this.fullText)
      .height(this.textHeight) // 绑定动态高度
      .textOverflow({ overflow: TextOverflow.Ellipsis })

    Button(this.isExpanded ? '收起' : '展开全文')
      .onClick(() => {
        animateTo({
          duration: 300, // 动画时长300ms
          curve: Curve.EaseOut
        }, () => {
          this.textHeight = this.isExpanded ? 60 : 200; // 切换目标高度
          this.isExpanded = !this.isExpanded;
        });
      })
  }
}
2. 动态计算文本高度

通过 TextMetrics 获取文本实际高度:

import { TextMetrics } from '@ohos.text';

@State textHeight: number = 0;

aboutToAppear() {
  const metrics = TextMetrics.measureText({
    text: this.fullText,
    fontSize: 16,
    maxWidth: '100%',
    maxLines: 3 // 折叠时的行数
  });
  this.textHeight = metrics.height;
}

三、优化方案

1. 文本分页加载

适用于超长文本(如小说):

@State visibleText: string = '';
@State pageSize: number = 500; // 每次加载500字符

loadMoreText() {
  this.visibleText = this.fullText.substring(0, this.visibleText.length + this.pageSize);
}

build() {
  Column() {
    Text(this.visibleText)
    Button('加载更多')
      .onClick(() => this.loadMoreText())
  }
}
2. 复用文本测量结果

缓存 TextMetrics 计算结果,避免重复测量:

private cachedHeight: number | null = null;

getTextHeight() {
  if (this.cachedHeight === null) {
    const metrics = TextMetrics.measureText({ /* ... */ });
    this.cachedHeight = metrics.height;
  }
  return this.cachedHeight;
}

四、完整案例:带动画的展开折叠

@Entry
@Component
struct AdvancedExpandableText {
  @State isExpanded: boolean = false;
  @State textHeight: number = 80; // 初始高度(约3行)
  @State fullText: string = '长文本内容...'.repeat(50);

  // 计算文本高度
  calcHeight() {
    const metrics = TextMetrics.measureText({
      text: this.fullText,
      fontSize: 16,
      maxWidth: '80%',
      maxLines: this.isExpanded ? undefined : 3
    });
    this.textHeight = metrics.height;
  }

  build() {
    Column() {
      // 文本容器(带动画高度)
      Stack() {
        Text(this.fullText)
          .fontSize(16)
          .width('80%')
          .height(this.textHeight)
          .textOverflow({ overflow: TextOverflow.Ellipsis })

        // 渐变遮罩(折叠时显示)
        if (!this.isExpanded) {
          Gradient()
            .colors(['#FFFFFF00', '#FFFFFFFF'])
            .height(30)
            .width('100%')
            .align(Alignment.Bottom)
        }
      }
      .margin(10)

      // 展开/折叠按钮
      Button(this.isExpanded ? $r('app.string.collapse') : $r('app.string.expand'))
        .onClick(() => {
          animateTo({
            duration: 300,
            curve: Curve.EaseInOut
          }, () => {
            this.isExpanded = !this.isExpanded;
            this.calcHeight(); // 重新计算高度
          });
        })
        .backgroundColor('#FFFFFF')
        .borderColor('#007DFF')
    }
  }
}

五、常见问题

问题原因解决方案
文本高度计算不准字体/间距未统一确保测量时与渲染样式一致
动画卡顿文本过长或设备性能差分页加载或减少动画复杂度
省略号不显示maxLines未设置检查maxLinestextOverflow

六、建议

  1. 设计规范

    • 折叠时保留 2-3行 文本,避免过度截断。
    • 按钮文案明确(如“展开全文”而非“更多”)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值