返回介绍

out 方法分析

发布于 2024-12-23 22:10:47 字数 5478 浏览 0 评论 0 收藏 0

out() 方法在我看来,就是 layout 中的 out。如果说 generate() 大部分是处理一些折行、段落相关的数据,那么 out() 方法就是将这些数据使用起来,真正地布局出来(注意,布局不是显示,显示的话还是在父类的 drawText() 方法中进行的)。

  1. 方法接收的参数如下所示,很多参数都是在 generate() 中处理获得,参数的含义和前面提到的基本相同。
    out(CharSequence text, int start, int end,
      int above, int below, int top, int bottom, int v,
      float spacingmult, float spacingadd,
      LineHeightSpan[] chooseHt, int[] chooseHtv,
      Paint.FontMetricsInt fm, int flags,
      boolean needMultiply, byte[] chdirs, int dir,
      boolean easy, int bufEnd, boolean includePad,
      boolean trackPad, char[] chs,
      float[] widths, int widthStart, TextUtils.TruncateAt ellipsize,
      float ellipsisWidth, float textWidth,
      TextPaint paint, boolean moreChars) 
    
  2. 对 mLineDirections 和 mLine 扩容处理,根据当前行数,判断下 mLine 数组大小是否足够储存当前行的信息,如果不够,则扩容,对应的 mLineDirections 也进行扩容处理(两个数组大小相同)。
     int j = mLineCount;
     int off = j * mColumns;
     int want = off + mColumns + TOP;
     int[] lines = mLines;
     if (want >= lines.length) {
       Directions[] grow2 = ArrayUtils.newUnpaddedArray(
           Directions.class, GrowingArrayUtils.growSize(want))
       System.arraycopy(mLineDirections, 0, grow2, 0,
                mLineDirections.length);
       mLineDirections = grow2;
       int[] grow = new int[grow2.length];
       System.arraycopy(lines, 0, grow, 0, lines.length);
       mLines = grow;
       lines = grow;
     }
    
  3. 待分析
     if (chooseHt != null) {
       fm.ascent = above;
       fm.descent = below;
       fm.top = top;
       fm.bottom = bottom;
       for (int i = 0; i < chooseHt.length; i++) {
         if (chooseHt[i] instanceof LineHeightSpan.WithDensity) {
           ((LineHeightSpan.WithDensity) chooseHt[i]).
             chooseHeight(text, start, end, chooseHtv[i], v, fm, paint);
         } else {
           chooseHt[i].chooseHeight(text, start, end, chooseHtv[i], v, fm);
         }
       }
       above = fm.ascent;
       below = fm.descent;
       top = fm.top;
       bottom = fm.bottom;
     }  
    
  4. 第一行和最后一行的特殊处理,以及行间距的处理
    // 判断是否是第一行
    boolean firstLine = (j == 0);
    // 判断是否是最后一行:全部文本的最后一行或者行数等于可见的最大的行数
    boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
    boolean lastLine = currentLineIsTheLastVisibleOne || (end == bufEnd);
    // 第一行需要处理上面的留白
    if (firstLine) {
       if (trackPad) {
         mTopPadding = top - above;
       }
       if (includePad) {
         above = top;
       }
    }
    int extra;
    // 最后一行需要处理下面的留白
    if (lastLine) {
      if (trackPad) {
        mBottomPadding = bottom - below;
      }
      if (includePad) {
        below = bottom;
      }
    }
    // 处理行间距
    if (needMultiply && !lastLine) {
       double ex = (below - above) * (spacingmult - 1) + spacingadd;
       if (ex >= 0) {
         extra = (int)(ex + EXTRA_ROUNDING);
       } else {
         extra = -(int)(-ex + EXTRA_ROUNDING);
       }
    } else {
       extra = 0;
    }
    
  5. 接下来就是记录每行的文本的信息,需要注意到的是,每行的信息由 lines 中的连续的值来记录,值的数量等于 mColumns 的大小( mColumns 的取值前面有提到)。
    // 记录每行的起止位置,顶部和底部位置
    lines[off + START] = start;
    lines[off + TOP] = v;
    lines[off + DESCENT] = below + extra;
    // 记录下一行的起始位置和顶部位置,v 值会作为返回值返回给调用的地方。
    v += (below - above) + extra;
    lines[off + mColumns + START] = end;
    lines[off + mColumns + TOP] = v;
    // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining
    // one bit for start field
    // 通过位运算记录 tab 和文本方向信息
    lines[off + TAB] |= flags & TAB_MASK;
    lines[off + HYPHEN] = flags;
    lines[off + DIR] |= dir << DIR_SHIFT;
    Directions linedirs = DIRS_ALL_LEFT_TO_RIGHT;
    // easy means all chars < the first RTL, so no emoji, no nothing
    // XXX a run with no text or all spaces is easy but might be an empty
    // RTL paragraph.  Make sure easy is false if this is the case.
    // 记录文本的方向
    if (easy) {
      mLineDirections[j] = linedirs;
    } else {
      mLineDirections[j] = AndroidBidi.directions(dir, chdirs, start - widthStart, chs,
          start - widthStart, end - start);
    }
    
  6. 文本省略的处理:
    // 判读是否需要省略
    if (ellipsize != null) {
      // If there is only one line, then do any type of ellipsis except when it is MARQUEE
      // if there are multiple lines, just allow END ellipsis on the last line
      boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount);
      boolean doEllipsis =
            (((mMaximumVisibleLineCount == 1 && moreChars) || (firstLine && !moreChars)) &&
                ellipsize != TextUtils.TruncateAt.MARQUEE) ||
            (!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
                ellipsize == TextUtils.TruncateAt.END);
      if (doEllipsis) {
        calculateEllipsis(start, end, widths, widthStart,
            ellipsisWidth, ellipsize, j,
            textWidth, paint, forceEllipsis);
      }
    }
    

    如 Google 的工程师注释所说的那样,如果是指定了最大行数是 1,则任何省略方式都可以,如果指定的最大行数不是 1,但是只有单行文本时,除了 MARQUEE 的省略方式不支持以外,其他的省略方式都是支持的。如果是多行省略,且不止一行文本时,只支持在可见的最后一行的最后省略,即 END 省略方式。

    省略的计算是通过 calculateEllipsis() 方法实现的,其内部处理完成会将省略的起始位置和计数复制给 mLines 对应的每行数据的第 5 和第 6 个数据(省略时每行的记录的数据个数为 6 个,即 mColumns 赋的值是 COLUMNS_ELLIPSIZE 的值,即 6), calculateEllipsis() 方法的实现这里就不作具体分析了。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文