Android TypedArray源码详解

在自定义控件的时候,如果我们想额外的添加一些属性,就会用到TypedArray这个类,那么这个类是怎么得到的,以及怎么使用的,这篇讲会详细讲解,下面是我以前自定义控件的一段代码

我们看到TypedArray是通过Context的方法得到的,但要记住完成之后一定要调用recycle()方法进行回收,我们点进去找到最终实现

我们先看下面AssetManager的applyStyle方法是native方法,也就是用C++实现的,他会提取自定义控件属性的的值保存TypedArray中的mData数组中,这个数组的大小是由你定义控件属性的个数决定的,是他的6倍,上面的attrs其实就是你自定义属性的个数,我们来看一下

他首先会从TypedArray池中获取,如果有就取出,mDate的大小不能小于属性个数的6倍,因为STYLE_NUM_ENTRIES的值为6,如果没有就new一个然后返回,把属性的值提取出来之后我们就可以来操作了,我们先来看一下View类初始化中的一段代码

他会把TypedArray中的数据提取出来对View的属性赋值,我们来看一下TypedArray类的构造方法

代码很简单,其中mData就是就是从xml文件中提取到的数据,mData的大小是自定义属性个数的6倍,所以这里是每6个作为一组,我们可以看一下上面的obtain方法中data数组的大小是乘以6(STYLE_NUM_ENTRIES)的,这6种类型如下,定义在AssetManager类中,下面的第一个表示每组6个

对应着TypedValue类中的这7中类型,其中string是根据type得到的

如果我们认真看的时候就会发现obtain方法中对mIndices数组初始化的时候是加1的,因为mIndices数组的第一个保存的是我们所使用属性的个数,记住是使用不是定义,我们来看一下其中的一些代码

第一个length返回的是我们所定义属性的个数,因为这个参数是在构造函数中赋值的,传递的是int[] attrs的长度,而这个sttrs就是我们在attrs文件中自定义属性的时候在R文件中自动生成的一个数组。而下面的getIndexCount()方法返回的是我们所使用的属性个数,因为mIndices的数据是从xml文件中提取的,第一个位置保存的是我们使用属性的个数,后面的位置就是我们使用的自定义属性在R文件中生成的id,在看一个方法,也是自定义的时候常用到的

这个得到的就是自定义属性在R文件中生成的id,剩下的一些方法就是从TypedArray中提取值了,主要有以下几种类型

TypedArray方法比较多,这里就捡常用的几个来分析一下,在分析之前先看一下下面这个方法

上面所说的每6个一组,其中每组下标为STYLE_ASSET_COOKIE(2)的是用来标记缓存的,并且是只对String类型的,我们来看一下

上面这个方法是把mData指定范围的6个数据提取到outValue中,其中string值通过type类型得到的,我们再来看一下assetCookie的注释

所以他只针对String类型,我们再来看一下String类型的注释

所以他只针对string类型的数据进行提取,比如text,String,color等,color可以是string类型也可以是int类型,还看上面的loadStringValueAt方法,如果cookie小于0,说明没有缓存,就会从xml中解析,否则就从缓存中取

我们来看一下是怎么从xml中解析的,看到上面的obtainStyledAttributes方法,会发现这样一段代码 array.mXml = parser;其中parser就是View及其子类在初始化的时候传递的AttributeSet,我们在前面的《Android LayoutInflater源码分析及使用(二)》中讲到,View及其子类创建的时候是通过反射来初始化的,我们来回顾一下

其中attrs是通过Resource的loadXmlResourceParser方法加载的,我们看一下

剩下的就是涉及到Xml的解析,这里就不在作深入探讨,言归正传,还回到刚才的loadStringValueAt方法,如果缓存中存在就从缓存中去,如果不存在就通过xml解析获取。下面在看一下一些常用的方法,其中getText(int index)和getString(int index)差不多,我们就来看一下getString(int index)方法

上面的index要乘以6(STYLE_NUM_ENTRIES),因为是每6个一组的,如果type为null就返回空,如果为String类型就会调用loadStringValueAt方法获取我们设置的值。有一点要注意,如果我们在attrs中设置的format类型和我们自定义设置的参数不符的话,当运行的时候是会报错的,必须要设置相符并clean才能解决。否则就执行下面的方法,强制转换为字符串,代码比较简单,这里就不再贴出。在来看下一个bool类型和int类型的,由于这两个差不多,就随便挑一个

上面的类型如果大于TYPE_FIRST_INT并且小于TYPE_LAST_INT的时候就从mDate中提取值,这个不知道为什么要这样写,不过从他的范围来看也就int,Boolean,color三种是这样取值的

如果范围不在TYPE_FIRST_INT和TYPE_LAST_INT之间,就会把mData指定位置上的值提取到TypedValue中,然后在强制转化,如果没有提取到就会返回一个默认值,因为如果在attrs中定义但没有用到,就会返回一个默认值。我们来看一下是怎么转化的

这个很好理解,转化为int类型有0开头的8进制,0x开头的16进制,还有#开头的color值,如果转化之前是负数,转化之后还要乘以-1(sign)。再来看一个

这个就不用多说了,因为color有String和int两种类型,如果是String类型就会返回ColorStateList的默认值,因为ColorStateList可能有好几种类型,但必须都是false的才是默认的,下面随便看一个,下面这些提取之后默认的就是green,因为只有他的所有状态都是false。

下面在看最后一个方法

看方法名大概就知道是获取layout的尺寸的,大致看一下,在ViewGroup中

其中获取到的值有3种,一种是精确的我们给的大于0的,一种是-1(MATCH_PARENT),另一种是-2(WRAP_CONTENT),记得在讲到《Android LayoutInflater源码分析及使用(二)》的时候说到,xml的属性除了宽和高以外在初始化的时候基本上都能提取到,但宽和高是不行的,因为他是最终计算出来的,如果大家自定义View继承View的时候,要必须重写onMeasure方法,重新计算他的宽和高,如果我们不计算,当我们使用MATCH_PARENT或WRAP_CONTENT属性的时候,结果是完全一样的,尺寸都是填满剩下的屏幕,如果不重写onMeasure方法,在xml文件中把他的宽和高都写死也行,但这样不够灵活,我们来看一下为什么要重写

我们看到View中的getDefaultSize方法,AT_MOST和EXACTLY返回的结果都是一样的,如果想看建议看一下ViewGroup的getChildMeasureSpec方法,这个就不在贴出,可以自己去看。OK,TypedArray中剩下的方法基本上也都非常相似,这里就不在一一讲述。

1 收藏 评论

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部