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的时候,就可以去选择性的去继承类了。
        

            

Android之TextView的Span样式源码剖析的更多相关文章

  1. Android之TextView的Span样式源代码剖析

    Android中的TextView是个显示文字的的UI类.在现实中的需求中,文字有各式各样的样式,TextView本身没有属性去设置实现.我们能够通过Android提供的 SpannableStrin ...

  2. Android 网络图片查看器与网页源码查看器

    在AndroidManifest.xml里面先添加访问网络的权限: <uses-permission android:name="android.permission.INTERNET ...

  3. 黎活明8天快速掌握android视频教程--24_网络通信之网页源码查看器

    1 该项目的主要功能就是从将后台的html网页在Android的界面上显示出来 后台就是建立一个java web工程在工程尚建立一个html或者jsp文件就可以了,这里主要看Android客户端的程序 ...

  4. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  5. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  6. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  7. Android版的菜谱客户端应用源码完整版

    Android版的菜谱客户端应用源码完整版,这个文章是从安卓教程网转载过来的,不是本人的原创,希望能够帮到大家的学习吧. <ignore_js_op> 152936qc7jdnv6vo0c ...

  8. Android源码剖析之Framework层升级版(窗口、系统启动)

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 看本篇文章之前,建议先查看: Android源码剖析之Framework层基础版 前面讲了frame ...

  9. 50个Android开发人员必备UI效果源码[转载]

    50个Android开发人员必备UI效果源码[转载] http://blog.csdn.net/qq1059458376/article/details/8145497 Android 仿微信之主页面 ...

随机推荐

  1. css之入门篇

    今日学习终于到了css,css可以实现很多表现出很酷的界面,而css的出现是为了解决 HTML结构上写样式出现一片混乱现象而应运而生的语言,在以前样式都是和结构一起写的, 不分彼此,而这样大大增加了代 ...

  2. Android 发布可穿戴设备 SDK 的开发者预览版

    今早上安卓官网查资料,发现网站上赫然显示着"Android Wear"几个大字.难道……?点进去看,果然,Android发布了可穿戴设备的SDK的开发者预览版. 其中这第五张图…… ...

  3. android: 服务的生命周期

    9.4   服务的生命周期 之前章节我们学习过了活动以及碎片的生命周期.类似地,服务也有自己的生命周期, 前面我们使用到的 onCreate().onStartCommand().onBind()和 ...

  4. AngularJS 模态对话框

    本文内容 项目结构 运行结果 index.html mymodal.js 参考资料 本文讲解 Angular JS 实现模式对话框.基于 AngularJS v1.5.3.Bootstrap v3.3 ...

  5. Base: 一种 Acid 的替代方案

    原文链接: BASE: An Acid Alternative Pdf下载链接: Base 数据库 ACID,都不陌生:原子性.一致性.隔离性和持久性,这在单台服务器就能搞定的时代,很容易实现,但是到 ...

  6. 外包采用Gradle生成多套app打包

    目的:可修改app名称.icon.包名.接口地址及其它 一.      修改基本配置(包名.版本号等) 配置module下的build.gradle 添加productFlavors例如: produ ...

  7. Swift中的Void类型与空元祖表达式

    可能有不少Swift开发者会忽略这么一个细节:在Swift中,Void类型其实是一个别名类型,而其真正的类型为(),即一个空元祖(empty tuple)! 这种语言特性给Swift带来了一些比较方便 ...

  8. JAVA常见错误处理方法 和 JVM内存结构

    OutOfMemoryError在开发过程中是司空见惯的,遇到这个错误,新手程序员都知道从两个方面入手来解决:一是排查程序是否有BUG导致内存泄漏:二是调整JVM启动参数增大内存.OutOfMemor ...

  9. SQL SERVER2012中使用游标来备份数据库

    在SQL SERVER中,添加JOB,可以使用以下语句设置定期备份数据库. DECLARE @name VARCHAR(50)--databasename DECLARE @path VARCHAR( ...

  10. “You must not call setTag() on a view Glide is targeting” 解决

    报错原因大致是因为Glide加载的iamgeView调用了setTag()方法导致的错误, 因为Glide已经默认为ImageView设置的Tag. 解决办法:自定义一个Application,在里面 ...