菜鸟进阶之深入理解android自定义属性(AttributeSet,TypedArray)

酝酿了很久,终于可以写篇关于android自定义属性的文章了,本来这篇文章的名字没有"菜鸟进阶之"的,然后发现这里的内容挺有难度,配得上这5个字了。

这里牵扯几个类比如AttributeSet、TypedArray,属性,xml文件等,内容有一点多,所以最重要的是理解每个东西是什么。

首先可以参考一文章:http://blog.csdn.net/ff313976/article/details/7949614,该文章展示了一个可以使用的自定义的属性的情况,这只是很多自定义属性中的一种情况而已,对于理解自定义属性还不够。

属性

自定义属性,首先要定义出来属性我们在attrs.xml文件里:

[html]  view plain  copy
 print ?
  1. xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <attr name="textSize0" format="dimension" />  
  4.     <declare-styleable name="button1">  
  5.         <attr name="textSize1" format="dimension" />  
  6.         <attr name="textSize2" format="dimension" />  
  7.     declare-styleable>  
  8.     <declare-styleable name="button2">  
  9.         <attr name="textSize3" format="dimension" />  
  10.         <attr name="textSize4" format="dimension" />  
  11.     declare-styleable>  
  12. resources>  
然后我们要看到产生什么效果:

在R.java文件里:

[java]  view plain  copy
 print ?
  1. public final class R {  
  2.     public static final class attr {  
  3.         public static final int textSize0=0x7f010000;  
  4.         public static final int textSize1=0x7f010001;  
  5.         public static final int textSize2=0x7f010002;  
  6.         public static final int textSize3=0x7f010003;  
  7.         public static final int textSize4=0x7f010004;  
  8.     }  
  9.     public static final class styleable {  
  10.         public static final int[] button1 = {  
  11.             0x7f0100010x7f010002  
  12.         };  
  13.         public static final int button1_textSize1 = 0;  
  14.         public static final int button1_textSize2 = 1;  
  15.         public static final int[] button2 = {  
  16.             0x7f0100030x7f010004  
  17.         };  
  18.         public static final int button2_textSize3 = 0;  
  19.         public static final int button2_textSize4 = 1;  
  20.     };  
  21. }  

我在这里把不相关的内容去掉了,在这里我们可以看到通过修改attrs.xml,R文件的改变是多了两个类,分别是attr类和styleable类,这里我们要注意的是区分出来这两个类,他们是不同的,后面获得TypedArray的时候他们的区别就会很明显。在我理解,attr就是属性呗,就想定义一个变量似的定义一个属性。styleable就是样式,就是属性的集合,在R文件里体现的很明显,button1就是样式,它包含两个属性的地址,就是0x7f010001和0x7f010002。还有一个值得注意的地方时button1_textSize1这个属性,它的作用就是下标。后面我们在TypedArray里取值的时候会用到。

AttributeSet

api的解释:

[plain]  view plain  copy
 print ?
  1. A collection of attributes, as found associated with a tag in an XML document. Often you will not want to use this interface directly, instead passing it to Resources.Theme.obtainStyledAttributes() which will take care of parsing the attributes for you. In particular, the Resources API will convert resource references (attribute values such as "@string/my_label" in the original XML) to the desired type for you; if you use AttributeSet directly then you will need to manually check for resource references (with getAttributeResourceValue(int, int)) and do the resource lookup yourself if needed. Direct use of AttributeSet also prevents the application of themes and styles when retrieving attribute values.   
这里只是粘了一部分过来,可以自己查看,反正AttributeSet这个类就是代表xml里一个节点下面的属性的集合,这个类一般都是系统在生成有xml配置的组件时生成,我们一般不去生成该对象。我们可以通过该对象操作xml里对应的属性,但是官方不建议这么使用,最直接的原因上面英文里有提到,就是它只是xml里属性的一个集合,没有做其他的处理,比如一个样式,这个类只是知道这个样式不能直接拿到样式里面的属性,其他原因不详。在这里展示一个粘一个直接使用AttributeSet获取属性的demo。

layout.xml文件的一部分:

[html]  view plain  copy
 print ?
  1. <com.example.drawableedittext.DrawableEditText  
  2.     android:id="@+id/text1"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="wrap_content"  
  5.     android:layout_centerHorizontal="true"  
  6.     hint="@string/hit"  
  7.     imgRes="@drawable/phonenumber_click"  
  8.     nullImgRes="@drawable/phonenumber" />  
java文件获取属性的一部分代码:

[java]  view plain  copy
 print ?
  1. // 设置文字大小  
  2.         int textSize = attrs.getAttributeResourceValue(null"textSize"0);  
  3.         if (textSize != 0) {  
  4.             mEditText.setTextSize(textSize);  
  5.         }  
至于该类其他的方法可自行查看api或者源码。在这里我们需要理解AttributeSet是什么,然后在使用的时候一般都是把AttributeSet封装成TypedArray进行使用。

TypedArray

我认为这个类是学习自定义属性最重要的,首先来看它是什么:

[java]  view plain  copy
 print ?
  1. Container for an array of values that were retrieved with Resources.Theme.obtainStyledAttributes(AttributeSet, int[], intint) or Resources.obtainAttributes. Be sure to call recycle when done with them. The indices used to retrieve values from this structure correspond to the positions of the attributes given to obtainStyledAttributes.  
它就是属性的集合,我们获取属性一般就是这个类的.getxxx()方法。

重点是学习这个类的实例是怎么来的?一般是由context.obtainStyledAttributes这个方法,有4个重载的方法。

我们来看

[java]  view plain  copy
 print ?
  1. TypedArray android.content.Context.obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)  

这个方法就是从资源里挑出一些属性来,按照顺序放到TypedArray里,参数可以控制从哪里挑选属性,挑选哪些。

参数set:挑选属性的出处是AttributeSet。

参数attrs:这是一个属性的数组,只是哪些属性被挑选出来,在之前看R文件的时候R.styleable.button1就是这样的数组,我们可以自己new这样的数组,再赋值。

参数defStyleAttr:挑选属性的出处是defStyleAttr。

参数defStyleRes:挑选属性的出处是defStyleRes。

[java]  view plain  copy
 print ?
  1. 通过第2个参数指定获取一些属性,获取到的是一个类似数组的结果,它的下标就像数组似的从0开始。在这里我们可以联想到之前在attrs.xml文件里定义的declare-styleable节点,定义这个节点会在R文件产生一个属性数组如button1,还会产生下标比如button1_textSize1 = 0,这样我们可以用R文件里的这两个属性来处理获取属性的这些操作。当然我们也可以new出来int的数组,下标自己写上去。  

获取TypedArray里数据的检索范围:

1)从AttributeSet获取。

PS:我在layout.xml里一个组件后面设置text属性,也在style设置text属性。在界面里使用的是在layout.xml设置的,但是用TypedArray获取,获取到的是在style里设置的,有待研究。

[java]  view plain  copy
 print ?
  1. TypedArray a = context.obtainStyledAttributes(set, new int[]{R.attr.textSize0,android.R.attr.text}, 00);  

2)从defStyleAttr获取。

使用这个方式检索属性,个人感觉稍微有点难理解,刚开始学习Android-ViewPagerIndicator这个库的时候,想修改那些tab的外观,所以才需要去学习自定义属性,该库使用的就是使用这个参数。这个参数是一个int,就是R文件里attr类里的一个属性,在R文件里这样:

[java]  view plain  copy
 print ?
  1. public final class R {  
  2.     public static final class attr {  
  3.         public static final int reference1=0x7f010001;  
  4.     }  
  5. }  
属性的名字随便,在attrs.xml文件里:

[html]  view plain  copy
 print ?
  1. xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <attr name="reference1" format="reference" />  
  4. resources>  
要求这个定义的属性是引用类型的。

什么意思呢?就是说使用这个方式,需要:

a、定义一个属性,该属性是应用类型的。

b、在AndroidManifest.xml里给activity或者application设置theme,在这个theme里包含一个属性,比如上面我们定义的reference1属性。这样这个theme里的reference1就对应着一组属性的集合。

c、在obtainStyledAttributes方法的第3个参数传入R文件里reference对应的值。

[java]  view plain  copy
 print ?
  1. TypedArray a = context.obtainStyledAttributes(nullnew int[]{R.attr.textSize0,android.R.attr.text}, R.attr.reference1, 0);  

作用:在该activity所在的theme里,查找R.attr.reference1属性对应的属性集合,在这个集合中检索出第2个参数所包含的属性,存放在返回值里。

3)从defStyleRes获取。

这个方式比较简单。如:

[java]  view plain  copy
 print ?
  1. TypedArray a = context.obtainStyledAttributes(nullnew int[]{R.attr.textSize0,android.R.attr.text}, 0, R.style.referecceres1);  
PS: 这里注意一下2)和3),他们一个是R.attr.,一个是R.style.,要分清他们是使用的不同方式获得的可被检索的属性集合。

至于从TypedArray里获得属性的值的一些方法,查看api就可以,没什么可研究的。

其他

1.关于xmlns。

在文章开始时推荐的那篇文章中就有关于添加一个xmlns的用法

[html]  view plain  copy
 print ?
  1. xmlns:myapp="http://schemas.android.com/apk/res/com.example.testtypedarray"  

这就是个命名空间,比如我们用安卓原生的属性的时候如果打错了,就会报错,这是因为那个属性下有哪些属性都是被规定好的,就在这里规定的

[html]  view plain  copy
 print ?
  1. xmlns:android="http://schemas.android.com/apk/res/android"  
可以看到

[html]  view plain  copy
 print ?
  1. mya="1234"  
这样添加属性是不出错的,因为它不被命名空间限制。出错时因为命名空间里没有你打错的那个属性。

设置这个只是在layout.xml里可以添加我们自定义的属性,和我们获取基本没关系。


补充:

1.关于TypedArray的获取方式,参看下面的官方的api:

[java]  view plain  copy
 print ?
  1. Return a StyledAttributes holding the attribute values in set that are listed in attrs. In addition, if the given AttributeSet specifies a style class (through the "style" attribute), that style will be applied on top of the base attributes it defines.   
  2.   
  3. Be sure to call StyledAttributes.recycle() when you are done with the array.   
  4.   
  5. When determining the final value of a particular attribute, there are four inputs that come into play:  
  6.   
  7. Any attribute values in the given AttributeSet.   
  8. The style resource specified in the AttributeSet (named "style").   
  9. The default style specified by defStyleAttr and defStyleRes   
  10. The base values in this theme.   
  11. Each of these inputs is considered in-order, with the first listed taking precedence over the following ones. In other words, if in the AttributeSet you have supplied "#ff000000">, then the button's text will always be black, regardless of what is specified in any of the styles.  
  12.   
  13. Parameters:  
  14. set The base set of attribute values. May be null.  
  15. attrs The desired attributes to be retrieved.  
  16. defStyleAttr An attribute in the current theme that contains a reference to a style resource that supplies defaults values for the StyledAttributes. Can be 0 to not look for defaults.  
  17. defStyleRes A resource identifier of a style resource that supplies default values for the StyledAttributes, used only if defStyleAttr is 0 or can not be found in the theme. Can be 0 to not look for defaults.  
  18. Returns:  
  19. Returns a TypedArray holding an array of the attribute values. Be sure to call TypedArray.recycle() when done with it.  

你可能感兴趣的:(安卓自定义控件)