- 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 源码解析
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
3. CharacterStyle
Span 有很多种,这里拿最简单的 CharacterStyle 来举例说明我们设置的 Span 最终是如何影响到文字绘制的。前面的代码中出现的 UnderlineSpan 就是 CharacterStyle 的子类之一,可以在 官网 上看到其他的子类。CharacterStyle 有一个抽象方法是 updateDrawState,下面是 UnderlineSpan 的简易版:
public class UnderlineSpan extends CharacterStyle implements UpdateAppearance, ParcelableSpan { ... @Override public void updateDrawState(TextPaint ds) { ds.setUnderlineText(true); } }
从这个实现来看,在文字绘制的时候会将绘制文字的 TextPaint 传进来,在这里更新其设置后就能实现对应的文字效果。从 TextView 源码分析 中可以看出在对 Span 的处理上分为两步:
TextLine tl = TextLine.obtain(); tl.set(paint, buf, start, end, dir, directions, hasTabOrEmoji, tabStops); tl.draw(canvas, x, ltop, lbaseline, lbottom);
通过 TextLine.obtain() 从 shared pool 中获取一个 TextLine(这么做的原因是这个对象本身比较大而且使用次数多,为了避免多次创建导致的频繁 GC),然后通过 TextLine.set 对 TextLine 进行初始化,最后 draw 方法绘制出文字。从源码可以看出在 set 的时候保存了 Span:
void set(TextPaint paint, CharSequence text, int start, int limit, int dir, Directions directions, boolean hasTabs, TabStops tabStops) { mPaint = paint; mText = text; mStart = start; mLen = limit - start; mDir = dir; mDirections = directions; if (mDirections == null) { throw new IllegalArgumentException("Directions cannot be null"); } mHasTabs = hasTabs; mSpanned = null; boolean hasReplacement = false; if (text instanceof Spanned) { mSpanned = (Spanned) text; // 保存 span mReplacementSpanSpanSet.init(mSpanned, start, limit); hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0; } ... }
然后是 draw 方法:
void draw(Canvas c, float x, int top, int y, int bottom) { if (!mHasTabs) { if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) { drawRun(c, 0, mLen, false, x, top, y, bottom, false); return; } if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) { drawRun(c, 0, mLen, true, x, top, y, bottom, false); return; } } ... } private float drawRun(Canvas c, int start, int limit, boolean runIsRtl, float x, int top, int y, int bottom, boolean needWidth) { if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) { float w = -measureRun(start, limit, limit, runIsRtl, null); handleRun(start, limit, limit, runIsRtl, c, x + w, top, y, bottom, null, false); return w; } return handleRun(start, limit, limit, runIsRtl, c, x, top, y, bottom, null, needWidth); } private float handleRun(int start, int measureLimit, int limit, boolean runIsRtl, Canvas c, float x, int top, int y, int bottom, FontMetricsInt fmi, boolean needWidth) { ... // 解析 Span mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit); ... for (int j = i, jnext; j < mlimit; j = jnext) { jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + inext) - mStart; int offset = Math.min(jnext, mlimit); wp.set(mPaint); // 遍历 Span for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) { // Intentionally using >= and <= as explained above if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + offset) || (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue; CharacterStyle span = mCharacterStyleSpanSet.spans[k]; span.updateDrawState(wp); // updateDrawState!!! } // Only draw hyphen on last run in line if (jnext < mLen) { wp.setHyphenEdit(0); } x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x, top, y, bottom, fmi, needWidth || jnext < measureLimit, offset); } ... }
几经周折到了 handleRun 这里,首先是 CharacterStyleSpanSet 的初始化,初始化的时候会将 mSpanned 中所带的 Span 都解析出来具体的过程可以看 SpanSet 的源码,然后使用 updateDrawState 更新 TextPaint 加入想要的效果,最后通过 handleText 绘制。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论