鸿蒙 HarmonyOS NEXT 自定义文本测量实现

自定义文本测量工具

以下是个人实现的一段文本测量的函数。代码是基于 HarmonyOS NEXT 实现的,用于获取显示满指定的文本组件,所需要的文本字符数。一般用在什么需求里呢,比如一章节小说,大概几千字,根据字号不同,可能需要二十甚至三十多页才能到下一章,在仿真翻页的情况下,一屏是固定大小的,就需要知道到底需要多少屏才能将所有文本存放。

typescript 复制
/**
   * 获取显示满指定文本组件所需要的文本字符数.
   * @param utils       文本测量工具对象
   * @param options     文本测量选项(需要跟目标 Text 组件的配置一致)
   * @param totalWidth  文本组件显示区域的宽度.
   * @param totalHeight 文本组件目标显示区域的高度.
   * @returns 铺满文本组件所需要的字符数.
   */
  static subStringMeasure(utils: MeasureUtils, options: MeasureOptions, totalWidth: number, totalHeight: Length): number {
    // 获取单行的行高
    options.maxLines = 1;
    let size: SizeOptions = utils.measureTextSize(options);
    // 行高和总高不存在则无法计算.
    if (!totalHeight || !size.height) {
      return 0;
    }
    // 计算需要的行数.
    let lineNum = Math.floor(totalHeight as number / (size.height as number));
    // 每行最多放的字符数(全为半角字符,且无其他字符间距之类的调整)
    if (!options.fontSize) {
      options.fontSize = '16fp';
    }
    // 计算一行最多需要多少个半角字符.
    let charPerLine = Math.floor(totalWidth / (options.fontSize as number) / 2);

    // 估算需要总的文本数量.
    let totalTextLength = charPerLine * lineNum;

    // 截取估算的文本数.
    let originText = options.textContent;
    options.textContent = originText.toString().substring(0, totalTextLength);
    // 最大行高为目标行的二倍
    options.maxLines = lineNum * 2;
    // 从最多的字符数开始依次递减
    let readyToBreak = false; // 一旦为 true,且 tmpLen不为 0 时说明已经匹配到.

    // 超出 100 结束匹配.
    let limit: number = 0;
    for (; limit < 100; ++limit) {
      // 计算当前的文本高度与目标文本高度的差所代表的行(最后一行不算,需要多算一行).
      let tmpLen = Math.floor(((utils.measureTextSize(options).height as number) - (totalHeight as number)) / (size.height as number) + 1);
      // 表示当前的文字正好达到指定高度最后一行.
      if (tmpLen === 0) {
        readyToBreak = true;
        // 行数一致,字符数需要填满最后一行.
        totalTextLength += 1;
      } else {
        if (readyToBreak) {
          // 如果已经匹配过,目前又不匹配,那么之前的匹配正好是一屏.
          totalTextLength -= 1;
          break;
        }
        // 表示文本过少,需要增加,每次增加(tmpLen * charPerLine/2)的数量.
        totalTextLength -= Math.floor(charPerLine / 2 * tmpLen);
      }
      options.textContent = originText.toString().substring(0, totalTextLength);
    }
    // 打印匹配到目标高度的次数.
    // console.log('measure text times: ' + limit);
    return totalTextLength;
  }