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