«

Android之TextView的Span样式源码剖析

时间:2024-3-2 19:45     作者:韩俊     分类: Android


Android中的TextView是个显示文字的的UI类,在现实中的需求中,文字有各式各样的样式,TextView本身没有属性去设置实现,我们可以通过Android提供的 SpannableString类封装。Android提供了很多的Span的类去实现样式,这个样式都是继承自CharacterStyle类。

 在上一篇博客中详细的介绍的怎么使用各种Span类,这篇博客主要是通过看源码,来分析Span的工作原理,内部的类结构,继承结构,从而达到我们自己可以自定义一个Span来使用。


    要想剖析Span的原理,我们就需要看懂TextView的大概的绘制流程,一个TextView中的类&#20284;是很复杂的,一点一点看源码,找顺序。<br style="margin:0px; padding:0px">

 首先,在CharcaterStyle类中具有

public abstract void updateDrawState(TextPaint tp);

方法,TextPaint是画笔,我个人认为TextPaint没啥作用,直接当作Paint去看就行了。既然updateDrawState需要Paint,那么就需要在TextView中的onDraw去调用这个方法,在onDraw方法中传递给画Text的画笔,这个方法才能起作用,那我们顺着看TextView中的onDraw方法,代码太多,我只贴关键代码。

在TextView的onDraw方法中只有下面的方法调用到了画笔。

Path highlight = getUpdatedHighlightPath();
    <strong>if</strong> (mEditor != <strong>null</strong>) {
        mEditor.onDraw(canvas, layout, highlight, mHighlightPaint,  

  cursorOffsetVertical);
    } <strong>else</strong> {
        layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
    }

    <strong>if</strong> (mMarquee != <strong>null</strong> &amp;&amp; mMarquee.shouldDrawGhost()) {
        canvas.translate((<strong>int</strong>) mMarquee.getGhostOffset(), 0.0f);
        layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
    }  <br style="margin:0px; padding:0px">

这里可以发现有两个类:Editor和Layout,TextView的onDraw就是在这两个类中去绘制的,继续分别看这两个类的作用。

1.Editor:还没找到出处代码。放下搁置以后再说

2.Layout:可以看到Layout有三个子类,BoringLayout、DynamicLayout、StaticLayout,这三个类是一些功能的封装,主要的实现还都是在Layout中,

我们看一下Layout中的代码:

public void draw(Canvas canvas, Path highlight, Paint highlightPaint,

        <strong>int</strong> cursorOffsetVertical) {

    <strong>final</strong> <strong>long</strong> lineRange = getLineRangeForDraw(canvas);

    <strong>int</strong> firstLine = TextUtils.unpackRangeStartFromLong(lineRange);

    <strong>int</strong> lastLine = TextUtils.unpackRangeEndFromLong(lineRange);

    <strong>if</strong> (lastLine &lt; 0) <strong>return</strong>;

    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 &lt; mMetricAffectingSpanSpanSet.numberOfSpans; j&#43;&#43;) {

            // Both intervals [spanStarts..spanEnds] and [mStart &#43; i..mStart &#43; mlimit] are NOT

            // empty by construction. This special case in getSpans() explains the &gt;= &amp; &lt;= tests

            if ((mMetricAffectingSpanSpanSet.spanStarts[j] &gt;= mStart &#43; mlimit) ||

                    (mMetricAffectingSpanSpanSet.spanEnds[j] &lt;= mStart &#43; 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的出处了。<br style="margin:0px; padding:0px">



我们可以总结一下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,

                 <strong>int</strong> start, <strong>int</strong> end, <strong>float</strong> x,
                 <strong>int</strong> top, <strong>int</strong> y, <strong>int</strong> bottom, Paint paint);

我们可以直接通过操作canvas去自己绘制,你想要怎么绘制,不就完全的听你的么???

分类之后,我们就可以了解到以后如果需要自定义Span的时候,就可以去选择性的去继承类了。


我的博客园地址:http://www.cnblogs.com/flyme2012/



        <p>版权声明:本文为博主原创文章,未经博主允许不得转载。</p>

标签: android

热门推荐