TextView中设置Span的源码浅析

来源:互联网 发布:multisim仿真软件开关 编辑:程序博客网 时间:2024/05/24 03:31
大家都知道Android在设计中可以实现最简单的图文混排,也就是通过在文本中添加各种各样的Span来实现一些效果,现在就来对TextView中如何将这些span转化为画布上的特色元素的代码进行简要的分析。
FrameWork中与这个操作有关系的主要是:TextView,DynamicLayout,Editor,Layout,TextLine,ParagraphStyle,CharacterStyle,ReplacementSpan等。
一切的变化缘起于TextView的onDraw,下面就从它开始走起:
protected void onDraw(Canvas canvas) {……if (mLayout == null) {            assumeLayout();        }……if (mEditor != null) {            mEditor.onDraw(canvas, layout, highlight, mHighlightPaint, cursorOffsetVertical);        } else {            layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);        }……}


看到最后其实是调用了Editor的onDraw来进行的实际工作,代码如下:实际上在方法assumeLayout中已经对layout进行了处理进行了不同的实例化,但是主线中使用的是父类的方法就不做分析了。
void onDraw(Canvas canvas, Layout layout, Path highlight, Paint highlightPaint,            int cursorOffsetVertical) {……if (mTextView.canHaveDisplayList() && canvas.isHardwareAccelerated()) {            drawHardwareAccelerated(canvas, layout, highlight, highlightPaint,                    cursorOffsetVertical);        } else {            layout.draw(canvas, highlight, highlightPaint, cursorOffsetVertical);        }}


最后的判断了一下是否执行硬件加速,硬件加速的绘制流程另文分析这里不做讲解,接下来看layout的draw方法:
public void draw(Canvas canvas, Path highlight, Paint highlightPaint,            int cursorOffsetVertical) {……drawBackground(canvas, highlight, highlightPaint, cursorOffsetVertical,                firstLine, lastLine);        drawText(canvas, firstLine, lastLine);}


很清晰,绘制背景和绘制文本内容,继续
public void drawText(Canvas canvas, int firstLine, int lastLine) {……TextLine tl = TextLine.obtain();…….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);}……}


看到这里才发现,实际的工作都是这个TextLine在完成的,set方法将paint,buf等传入,draw方法绘制
void set(TextPaint paint, CharSequence text, int start, int limit, int dir,            Directions directions, boolean hasTabs, TabStops tabStops) {        mPaint = paint;        mText = text;        ……        mSpanned = null;        boolean hasReplacement = false;        if (text instanceof Spanned) {            mSpanned = (Spanned) text;            mReplacementSpanSpanSet.init(mSpanned, start, limit);            hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0;        }……}


注意一下mPaint,mText和mSpanned这三个对象,下面的变换操作都依赖它们。
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) {……if (mSpanned == null) {            TextPaint wp = mWorkPaint;            wp.set(mPaint);            final int mlimit = measureLimit;            return handleText(wp, start, mlimit, start, limit, runIsRtl, c, x, top,                    y, bottom, fmi, needWidth || mlimit < measureLimit);}mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit);mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit);……TextPaint wp = mWorkPaint;            wp.set(mPaint);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);                }            }            if (replacement != null) {                x += handleReplacement(replacement, wp, i, mlimit, runIsRtl, c, x, top, y,                        bottom, fmi, needWidth || mlimit < measureLimit);                continue;            }……for (int j = i, jnext; j < mlimit; j = jnext) {                jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + mlimit) -                        mStart;                wp.set(mPaint);                for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) {                    // Intentionally using >= and <= as explained above                    if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + jnext) ||                            (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue;                    CharacterStyle span = mCharacterStyleSpanSet.spans[k];                    span.updateDrawState(wp);                }                x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x,                        top, y, bottom, fmi, needWidth || jnext < measureLimit);            }……}


下面着重分析一下这个方法,绘制文本和文本中的一些文字风格字体颜色等等主要是通过handleText来完成的,主要变换的东西就是wp这个,它包含了之前传入的mPaint,如果处理的span类型是CharacterStyle,就执行span的updateDrawState方法,来修改绘制的mPaint的属性,然后调用handleText绘制文字(比如AbsoluteSizeSpan中修改的就是TextSize)。其他的如果是ReplacementSpan类型的,调用handleReplacement来进行replace处理,最终将调用到replacement.draw()来实现绘制replace元素。比如imageSpan中就是绘制其中的mDrawable。

噢噢,忘记前面还有关于ParagraphStyle的处理了,相信大家在看源码时会看到的,看看就清楚了,我就等有空再讲其补上了。。。

0 0
原创粉丝点击