如何实现文本展开收起功能
============

使用[measureTextSize](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-uicontext-measureutils#measuretextsize12)接口实现文字段落展开收起的功能，具体实现如下所示：

_// Text line width_
const TEXT\_WIDTH: number = 350;
_// Number of lines to show when collapsed_
const COLLAPSE\_LINES: number = 2;
const ELLIPSIS: string = '...';
const EXPAND\_STR: string = 'Expand';
const COLLAPSE\_STR: string = 'Collapse';
const FULL\_TEXT: string =
  'HarmonyOS provides a UI development framework called the ArkUI Framework. The ArkUI Framework provides developers with ' +
    'essential capabilities for application UI development, such as multiple components, layout calculations, animation capabilities, UI interaction, drawing, etc.\\n' +
    'The ArkUI Framework offers two development paradigms for developers with different purposes and technical backgrounds: ' +
    'the Declarative Development Paradigm based on ArkTS (referred to as the "Declarative Development Paradigm") ' +
    'and the JS-compatible Web Development Paradigm (referred to as the "Web Development Paradigm"). Here is a simple comparison of these two development paradigms.'

@Entry
@Component
struct TextCollapseTest {
  @State title: string = FULL\_TEXT;
  @State suffixStr: string = '';
  private expanded: boolean = true;
  private needProcess: boolean = true;

  aboutToAppear(): void {
    this.process();
  }

  process(): void {
    if (this.expanded) {
      this.collapseText();
    } else {
      this.expandText();
    }
  }

 _ // Expand text_
  expandText(): void {
    if (this.needProcess) {
      this.suffixStr = COLLAPSE\_STR;
      this.expanded = true;
      this.title = FULL\_TEXT;
    }
  }

 _ // Collapse text_
  collapseText(): void {
    if (!this.needProcess) {
      this.suffixStr = '';
      return;
    }
  _  // Size of expanded text_
    let expandSize: SizeOptions = this.getUIContext().getMeasureUtils().measureTextSize({
      textContent: FULL\_TEXT,
      constraintWidth: TEXT\_WIDTH,
      fontSize: 30
    });

    _// Size of text to be collapsed_
    let collapseSize: SizeOptions = this.getUIContext().getMeasureUtils().measureTextSize({
      textContent: FULL\_TEXT,
      constraintWidth: TEXT\_WIDTH,
      fontSize: 30,
      maxLines: COLLAPSE\_LINES
    });

  _  // No processing needed when collapsed and expanded text heights are equal_
    if (!expandSize || !collapseSize || (expandSize.height as number) == (collapseSize.height as number)) {
      this.needProcess = false;
      return;
    }

    let clipTitle: string = FULL\_TEXT;
    this.suffixStr = EXPAND\_STR;
  _  // Use binary search to find string length that fits exactly two lines_
    let leftCursor: number = 0;
    let rightCursor: number = this.title.length;
    let cursor: number = Math.floor(rightCursor / 2);
    let tempTitle: string = '';
  _  // Binary search to find the maximum text length fitting exactly two lines_
    while (true) {
      tempTitle = this.title.substring(0, cursor) + ELLIPSIS + EXPAND\_STR;
      const currentLinesTextSize: SizeOptions = this.getUIContext().getMeasureUtils().measureTextSize({
        textContent: tempTitle,
        fontSize: 30,
        wordBreak: WordBreak.BREAK\_ALL,
        constraintWidth: TEXT\_WIDTH
      });

      if ((currentLinesTextSize.height as number) > (collapseSize.height as number)) {
     _   // Current text exceeds two lines, continue searching left_
        rightCursor = cursor;
        cursor = leftCursor + Math.floor((cursor - leftCursor) / 2);
      } else {
       _ // Current text less than two lines, might be ok but still need to search right_
        leftCursor = cursor;
        cursor += Math.floor((rightCursor - cursor) / 2);
      }
      if (Math.abs(rightCursor - leftCursor) <= 1) {
       _ // Pointers almost overlap, meaning we found the position_
        break;
      }
    }
    clipTitle = this.title.substring(0, cursor - 1);
    this.title = clipTitle + ELLIPSIS;
    this.expanded = false;
  }

  build() {
    Row() {
      Column() {
        Text() {
          Span(this.title)
          if (this.needProcess) {
            Span(this.suffixStr)
              .fontColor(Color.Orange)
              .onClick(() => {
                this.process();
              })
          }
        }
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
        .width(TEXT\_WIDTH)
      }
      .width('100%')
    }
    .height('100%')
  }
}