天天看點

深入了解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.             0x7f010001, 0x7f010002  
  12.         };  
  13.         public static final int button1_textSize1 = 0;  
  14.         public static final int button1_textSize2 = 1;  
  15.         public static final int[] button2 = {  
  16.             0x7f010003, 0x7f010004  
  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[], int, int) 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}, 0, 0);  

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(null, new 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(null, new 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. Be sure to call StyledAttributes.recycle() when you are done with the array.   
  3. When determining the final value of a particular attribute, there are four inputs that come into play:  
  4. Any attribute values in the given AttributeSet.   
  5. The style resource specified in the AttributeSet (named "style").   
  6. The default style specified by defStyleAttr and defStyleRes   
  7. The base values in this theme.   
  8. 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 <Button textColor="#ff000000">, then the button's text will always be black, regardless of what is specified in any of the styles.  
  9. Parameters:  
  10. set The base set of attribute values. May be null.  
  11. attrs The desired attributes to be retrieved.  
  12. 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.  
  13. 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.  
  14. Returns:  
  15. Returns a TypedArray holding an array of the attribute values. Be sure to call TypedArray.recycle() when done with it.  

轉自:http://blog.csdn.net/bingospunky/article/details/39890053

繼續閱讀