Android中如何獲取xml界面裏的非自定義屬性

原文地址


獲取自定義屬性大家都很熟悉了,就不多說了(定義declare-styleable,context.obtainStyledAttributes巴拉巴拉...)

下面我們說一下怎麼獲取非自定義的屬性,比如android:entries,android:gravity等等,上面的方式照着套是行不通了,因爲你無法取得android.R.styleable裏面的東西,下面是我試出來的幾種方法:

1. 遍歷AttributeSet,基本可以把所有xml裏的屬性取出來,但是有一點,取出來的值要能使用,需要經過一定處理,具體代碼如下:

複製代碼
 1         //find entries
 2         int count = attrs.getAttributeCount();
 3         for (int i = 0; i < count; i++) {
 4             if ("entries".equals(attrs.getAttributeName(i))) {
 5                 int resid = attrs.getAttributeResourceValue(i, 0);
 6                 if (resid > 0) {
 7                     String[] entries = context.getResources().getStringArray(resid);
 8                     if (entries != null) {
 9                         //get gravity
10                         int g = -1;
11                         for (int j = 0; j < count; j++) {
12                             if ("gravity".equals(attrs.getAttributeName(j))) {
13                                 g = j;
14                                 break;
15                             }
16                         }
17                         final int gravity = attrs.getAttributeIntValue(g, Gravity.CENTER);
18                         //create adapter
19                         ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(context, R.layout.item_spinner_top, R.id.text, entries) {
20                             @Override
21                             public View getView(int position, View convertView, ViewGroup parent) {
22                                 View view = super.getView(position, convertView, parent);
23                                 TextView text = ButterKnife.findById(view, R.id.text);
24                                 text.setTextColor(textColor);
25                                 text.setTextSize(DimenUtils.px2dp(textSize));
26                                 text.setGravity(gravity);
27                                 return view;
28                             }
29 
30                             @Override
31                             public View getDropDownView(int position, View convertView, ViewGroup parent) {
32                                 View dropDownView = super.getDropDownView(position, convertView, parent);
33                                 TextView text = ButterKnife.findById(dropDownView, R.id.text);
34                                 text.setTextColor(dropdownTextColor);
35                                 text.setTextSize(DimenUtils.px2dp(textSize));
36                                 text.setGravity(gravity);
37                                 return dropDownView;
38                             }
39                         };
40                         adapter.setDropDownViewResource(R.layout.item_spinner_dropdown);
41                         setAdapter(adapter);
42                     }
43                 }
44                 break;
45             }
46         }
複製代碼

2. 還是使用context.obtainStyledAttributes,數組可以從android.R.attr裏面取(不需要styleable了),處理起來跟以前差不多,很好用,推薦

複製代碼
    private static final int[] RX_SPINNER_OVERRIDE_ATTRS = {
            android.R.attr.entries,
            android.R.attr.gravity
    };

        a = context.obtainStyledAttributes(attrs,RX_SPINNER_OVERRIDE_ATTRS);
        if (a.hasValue(0)){
            CharSequence[] entries = a.getTextArray(0);
            final int gravity = a.getInt(1,Gravity.CENTER);
            //create adapter
            ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(context, R.layout.item_spinner_top, R.id.text, entries) {
                @Override
                public View getView(int position, View convertView, ViewGroup parent) {
                    View view = super.getView(position, convertView, parent);
                    TextView text = ButterKnife.findById(view, R.id.text);
                    text.setTextColor(textColor);
                    text.setTextSize(DimenUtils.px2dp(textSize));
                    text.setGravity(gravity);
                    return view;
                }

                @Override
                public View getDropDownView(int position, View convertView, ViewGroup parent) {
                    View dropDownView = super.getDropDownView(position, convertView, parent);
                    TextView text = ButterKnife.findById(dropDownView, R.id.text);
                    text.setTextColor(dropdownTextColor);
                    text.setTextSize(DimenUtils.px2dp(textSize));
                    text.setGravity(gravity);
                    return dropDownView;
                }
            };
            adapter.setDropDownViewResource(R.layout.item_spinner_dropdown);
            setAdapter(adapter);
        }
        a.recycle();
複製代碼

 

 在使用中無意發現另外一個問題,tint相關屬性即使設置了,在5.0以下也是拿不到的,究其原因應該是這個是api level 21才引入的,低於這個的機器上被忽略了,那麼要同時兼容高低版本的話,可以使用v7包裏的tint屬性,android.support.v7.appcompact.R.attr.backgroundTint等,使用的時候就不要用android:backgroundTint了,改用app:backgroundTint即可。

 

9月17日更新

     抱歉,上面的代碼沒有經過詳細測試,實際上只能拿到entries的值,拿不到gravity的值,我也很奇怪,百思不得其解,照理說應該是同樣的模式,追蹤代碼追到最後是一個native方法...

     然後我去stackoverflow上提了問題(http://stackoverflow.com/questions/32602982/can-get-entries-but-not-gravity/),嘗試讓國外大神幫我看下,然而並沒有人回答,今天花了一天時間找了半天原因,總算是找到了,記錄一下。

     gravity不能獲取的原因是,v7包裏,它被重新定義了!

    

<declare-styleable name="Spinner"><attr format="reference" name="prompt"/><attr format="enum" name="spinnerMode"><enum name="dialog" value="0"/><enum name="dropdown" value="1"/></attr><attr name="android:dropDownSelector"/><attr name="android:popupBackground"/><attr name="android:dropDownVerticalOffset"/><attr name="android:dropDownHorizontalOffset"/><attr name="android:dropDownWidth"/><attr format="reference" name="popupPromptView"/><attr name="android:gravity"/><attr format="boolean" name="disableChildrenWhenDisabled"/><attr name="android:background"/></declare-styleable>

    注意裏面重新定義了一些屬性(比如<attr name="android:gravity"/>),可能由於這個原因,上面的獲取方法失效了.

    解決辦法也很簡單,從v7包裏取就可以了!v7包是可以訪問styleable的,這樣就能使用常規方法來取了!

    

1     a = context.obtainStyledAttributes(attrs,android.support.v7.appcompat.R.styleable.Spinner);
2     final int gravity = a.getInt(android.support.v7.appcompat.R.styleable.Spinner_android_gravity,Gravity.CENTER);
3     a.recycle();

    ok,測試通過!

    更進一步

    通過v7兼容包裏的寫法,我們知道了,通過定義<attr name="android:gravity"/>是可以重定義android原生屬性的(不知道重定義在這裏用得準不準確,反正是這麼個意思,大家理解就可以了)

    那麼假如你有個自定義組件,想使用類似android:gravity,android:xxx之類的屬性,實現一些其他功能或者可定義性,那麼完全可以自己在attr裏面定義,只需要寫上name就行了,不需要設置format,還有枚舉什麼的一大堆,全部是自動繼承的!

    然後xml裏就還是跟以前一樣設置,android:xxx=yyy,java裏面可以跟自定義屬性一樣取,簡單方便!下面上個例子

    

複製代碼
1     <declare-styleable name="RxSpinner">
2         <attr name="text_size" format="dimension"/>
3         <attr name="text_color" format="color" />
4         <attr name="dropdown_text_color" format="color"/>
5         <attr name="android:gravity" />
6         <attr name="android:entries" />
7     </declare-styleable>
複製代碼
複製代碼
1         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RxSpinner);
2         final int textColor = a.getColor(R.styleable.RxSpinner_text_color, context.getResources().getColor(R.color.gray_dark));
3         final float textSize = a.getDimension(R.styleable.RxSpinner_text_size, context.getResources().getDimension(R.dimen.text_normal));
4         final int dropdownTextColor = a.getColor(R.styleable.RxSpinner_dropdown_text_color, context.getResources().getColor(R.color.gray_dark));
5         final int gravity = a.getInt(R.styleable.RxSpinner_android_gravity,Gravity.CENTER);     //get android:gravity
6         final CharSequence[] entries = a.getTextArray(R.styleable.RxSpinner_android_entries);   //get android:entries
7         a.recycle();
複製代碼

     怎麼樣,是不是很好用,蛤蛤。

     折騰這麼多天,總算弄清楚了,找到了一種簡潔的寫法。其實一開始我就在想,有沒有什麼辦法,讓自定義組件的屬性直接繼承,後面走了不少彎路,看了v7源碼才知道,可以這麼寫,原來就是加一個android命名空間的事,源碼還是很有參考價值的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章