【Android之TextView的Span样式源代码剖析】贵有恒,何必三更起、五更眠、最无益,只怕一日曝、十日寒。这篇文章主要讲述Android之TextView的Span样式源代码剖析相关的知识,希望能为你提供帮助。
android中的TextView是个显示文字的的UI类。在现实中的需求中,文字有各式各样的样式,TextView本身没有属性去设置实现。我们能够通过Android提供的
SpannableString类封装。Android提供了非常多的Span的类去实现样式,这个样式都是继承自CharacterStyle类。
在上一篇博客中具体的介绍的怎么使用各种Span类,这篇博客主要是通过看源代码,来分析Span的工作原理。内部的类结构。继承结构。从而达到我们自己能够自己定义一个Span来使用。
要想剖析Span的原理。我们就须要看懂TextView的大概的绘制流程。一个TextView中的类似是非常复杂的。一点一点看源代码。找顺序。
首先,在CharcaterStyle类中具有
public
abstract
void
updateDrawState(TextPaint
tp);
方法,TextPaint是画笔,我个人觉得TextPaint没啥作用,直接当作Paint去看即可了。
既然updateDrawState须要Paint,那么就须要在TextView中的onDraw去调用这种方法,在onDraw方法中传递给画Text的画笔。这种方法才干起作用,那我们顺着看TextView中的onDraw方法,代码太多。我仅仅贴关键代码。
在TextView的onDraw方法中仅仅有以下的方法调用到了画笔。
Path
highlight
=
getUpdatedHighlightPath();
if
(mEditor
!=
null)
{
mEditor.onDraw(canvas,
layout,
highlight,
mHighlightPaint,
cursorOffsetVertical);
}
else
{
layout.draw(canvas,
highlight,
mHighlightPaint,
cursorOffsetVertical);
}
if
(mMarquee
!=
null
&
&
mMarquee.shouldDrawGhost())
{
canvas.translate((int)
mMarquee.getGhostOffset(),
0.0f);
layout.draw(canvas,
highlight,
mHighlightPaint,
cursorOffsetVertical);
}
这里能够发现有两个类:Editor和Layout,TextView的onDraw就是在这两个类中去绘制的,继续分别看这两个类的作用。1.Editor:还没找到出处代码。放下搁置以后再说2.Layout:能够看到Layout有三个子类。BoringLayout、DynamicLayout、StaticLayout,这三个类是一些功能的封装,基本的实现还都是在Layout中,我们看一下Layout中的代码:
public
void
draw(Canvas
canvas,
Path
highlight,
Paint
highlightPaint,
int
cursorOffsetVertical)
{
final
long
lineRange
=
getLineRangeForDraw(canvas);
int
firstLine
=
TextUtils.unpackRangeStartFromLong(lineRange);
int
lastLine
=
TextUtils.unpackRangeEndFromLong(lineRange);
if
(lastLine
<
0)
return;
drawBackground(canvas,
highlight,
highlightPaint,
cursorOffsetVertical,
firstLine,
lastLine);
drawText(canvas,
firstLine,
lastLine);
}drawBackground 绘制背景
drawText
绘制文字
找到了关键的代码了。接着看drawText中的源代码:
if
(directions
==
DIRS_ALL_LEFT_TO_RIGHT
&
&
!mSpannedText
&
&
!hasTabOrEmoji)
{
//
XXX:
assumes
there\'s
nothing
additional
to
be
done
canvas.drawText(buf,
start,
end,
x,
lbaseline,
paint);
}
else
{
tl.set(paint,
buf,
start,
end,
dir,
directions,
hasTabOrEmoji,
tabStops);
tl.draw(canvas,
x,
ltop,
lbaseline,
lbottom);
}
能够看到的是有个推断条件的。直接就能够绘制文字的。可是我们还没找到有关Span的代码啊,难道没有,不要着急。还有tl.draw。看源代码:
ReplacementSpan replacement = null;for (int j = 0; j < mMetricAffectingSpanSpanSet.numberOfSpans; j++) { // Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT // empty by construction. This special case in getSpans() explains the > = & < = tests if ((mMetricAffectingSpanSpanSet.spanStarts[j] > = mStart + mlimit) || (mMetricAffectingSpanSpanSet.spanEnds[j] < = mStart + i)) continue; MetricAffectingSpan span = mMetricAffectingSpanSpanSet.spans[j]; if (span instanceof ReplacementSpan) { replacement = (ReplacementSpan)span; } else { // We might have a replacement that uses the draw // state, otherwise measure state would suffice. span.updateDrawState(wp); } } Ok ,最终找到了Span的出处了。
我们能够总结一下TextView绘制流程了。
TextView的onDraw----》Layout的draw----》TextLine的Draw----》CharacterStyle的updateDrawState(假设设置的有Span样式)
绘制的基本的代码还是在Layout的Draw中和TextLine的Draw中。
从类的继承结构图中我简单的CharacterStyle分为两类:一个是直接继承CharacterStyle的,还有一个ReplacementSpan。第一种:直接继承CharacterStyle的样式是主要跟Paint相关的。仅仅须要更改画笔中的设置就可以达到更改目的的。
另外一种:继承ReplacementSpan的。在ReplacementSpan中有Draw的方法, public abstract void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint); 我们能够直接通过操作canvas去自己绘制,你想要怎么绘制,不就全然的听你的么??? 分类之后。我们就能够了解到以后假设须要自己定义Span的时候。就能够去选择性的去继承类了。
我的博客园地址:http://www.cnblogs.com/flyme2012/
推荐阅读
- androidHandlerr.removeCallbacksAndMessages(null)的妙用
- wpf之StackPanelWrapPanelWrapPanel之间的关系
- android studio java.io.IOException:setDataSourse fail.
- Android仿华为天气绘制刻度盘
- Android -- 贝塞尔实现水波纹动画(划重点!!)
- Alamofire +ObjectMapper模型: 上传单张图片,上传多张图片。
- word 2010 样式默认值的设置_Word专区
- word 2010 运用文本框完成纵横混排_Word专区
- WPS Office与微软Office精确兼容 免费办公软件便捷实用_WPS office