- CompoundButton 源码分析
- LinearLayout 源码分析
- SearchView 源码解析
- LruCache 源码解析
- ViewDragHelper 源码解析
- BottomSheets 源码解析
- Media Player 源码分析
- NavigationView 源码解析
- Service 源码解析
- Binder 源码分析
- Android 应用 Preference 相关及源码浅析 SharePreferences 篇
- ScrollView 源码解析
- Handler 源码解析
- NestedScrollView 源码解析
- SQLiteOpenHelper/SQLiteDatabase/Cursor 源码解析
- Bundle 源码解析
- LocalBroadcastManager 源码解析
- Toast 源码解析
- TextInputLayout
- LayoutInflater 和 LayoutInflaterCompat 源码解析
- TextView 源码解析
- NestedScrolling 事件机制源码解析
- ViewGroup 源码解析
- StaticLayout 源码分析
- AtomicFile 源码解析
- AtomicFile 源码解析
- Spannable 源码分析
- Notification 之 Android 5.0 实现原理
- CoordinatorLayout 源码分析
- Scroller 源码解析
- SwipeRefreshLayout 源码分析
- FloatingActionButton 源码解析
- AsyncTask 源码分析
- TabLayout 源码解析
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
out 方法分析
out()
方法在我看来,就是 layout 中的 out。如果说 generate()
大部分是处理一些折行、段落相关的数据,那么 out()
方法就是将这些数据使用起来,真正地布局出来(注意,布局不是显示,显示的话还是在父类的 drawText()
方法中进行的)。
- 方法接收的参数如下所示,很多参数都是在
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)
- 对 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; }
- 待分析
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; }
- 第一行和最后一行的特殊处理,以及行间距的处理
// 判断是否是第一行 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; }
- 接下来就是记录每行的文本的信息,需要注意到的是,每行的信息由 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); }
- 文本省略的处理:
// 判读是否需要省略 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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论