深入理解Android中的自定義屬性

本來想寫一篇關(guān)于自定義View中自定義屬性的詳解文章的臭杰,結(jié)果發(fā)現(xiàn)了洋大神的這篇文章蔬咬,很詳細鲤遥,分析的很透徹。本著不重復(fù)造輪子的思想林艘,分享這一篇大神的文章盖奈。

本文出自:【洋神的博客】

1、引言

對于自定義屬性狐援,大家肯定都不陌生钢坦,遵循以下幾步究孕,就可以實現(xiàn):

1,自定義一個CustomView(extends View )類

2爹凹,編寫values/attrs.xml厨诸,在其中編寫styleable和item等標簽元素

3,在布局文件中CustomView使用自定義的屬性(注意namespace)

4禾酱,在CustomView的構(gòu)造方法中通過TypedArray獲取

那么微酬,我有幾個問題:

  • 以上步驟是如何奏效的?

  • styleable 的含義是什么宇植?可以不寫嘛得封?我自定義屬性,我聲明屬性就好了指郁,為什么一定要寫個styleable呢忙上?

  • 如果系統(tǒng)中已經(jīng)有了語義比較明確的屬性,我可以直接使用嘛闲坎?

  • 構(gòu)造方法中的有個參數(shù)叫做AttributeSet
    (eg: MyTextView(Context context, AttributeSet attrs) )這個參數(shù)看名字就知道包含的是參數(shù)的數(shù)組疫粥,那么我能不能通過它去獲取我的自定義屬性呢?

  • TypedArray是什么鬼腰懂?從哪冒出來的梗逮,就要我去使用?

2绣溜、常見的例子###

接下來通過例子來回答上述問題慷彤,問題的回答順序不定~~大家先看一個常見的例子,即上述幾個步驟的代碼化怖喻。

自定義屬性的聲明文件

     <?xml version="1.0" encoding="utf-8"?>
     <resources>
    
    <declare-styleable name="test">
    <attr name="text" format="string" />
    <attr name="testAttr" format="integer" />
    </declare-styleable>
    
     </resources>

自定義的一個View類

package com.example.test;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

public class MyTextView extends View {

    private static final String TAG = MyTextView.class.getSimpleName();

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.test);

        String text = ta.getString(R.styleable.test_testAttr);
        int textAttr = ta.getInteger(R.styleable.test_text, -1);

        Log.e(TAG, "text = " + text + " , textAttr = " + textAttr);

        ta.recycle();
    }

}

布局文件中使用

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:zhy="http://schemas.android.com/apk/res/com.example.test"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.example.test.MyTextView
        android:layout_width="100dp"
        android:layout_height="200dp"
        zhy:testAttr="520"
        zhy:text="helloworld" />

</RelativeLayout>

大家花3s掃一下底哗,運行結(jié)果為:

MyTextView: text = helloworld , textAttr = 520

應(yīng)該都不意外吧,注意下锚沸,我的styleable的name寫的是test跋选,所以說這里并不要求一定是自定義View的名字。

3哗蜈、AttributeSet與TypedArray###

下面考慮:

構(gòu)造方法中的有個參數(shù)叫做AttributeSet(eg: MyTextView(Context context, AttributeSet attrs) )這個參數(shù)看名字就知道包含的是參數(shù)的集合前标,那么我能不能通過它去獲取我的自定義屬性呢?

首先AttributeSet中的確保存的是該View聲明的所有的屬性距潘,并且外面的確可以通過它去獲攘读小(自定義的)屬性,怎么做呢音比?
其實看下AttributeSet的方法就明白了俭尖,下面看代碼。

public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

        int count = attrs.getAttributeCount();
        for (int i = 0; i < count; i++) {
            String attrName = attrs.getAttributeName(i);
            String attrVal = attrs.getAttributeValue(i);
            Log.e(TAG, "attrName = " + attrName + " , attrVal = " + attrVal);
        }

        // ==>use typedarray ...

    }

輸出:

MyTextView(4136): attrName = layout_width , attrVal = 100.0dip
MyTextView(4136): attrName = layout_height , attrVal = 200.0dip
MyTextView(4136): attrName = text , attrVal = helloworld
MyTextView(4136): attrName = testAttr , attrVal = 520

結(jié)合上面的布局文件硅确,你發(fā)現(xiàn)了什么目溉?
我擦明肮,果然很神奇,真的獲得所有的屬性缭付,恩柿估,沒錯,通過AttributeSet可以獲得布局文件中定義的所有屬性的key和value(還有一些方法陷猫,自己去嘗試)秫舌,那么是不是說TypedArray這個鬼可以拋棄了呢?答案是:NO!绣檬。

現(xiàn)在關(guān)注下一個問題:

TypedArray是什么鬼足陨?從哪冒出來的,就要我去使用娇未?

我們簡單修改下墨缘,布局文件中的MyTextView的屬性。

<com.example.test.MyTextView
        android:layout_width="@dimen/dp100"
        android:layout_height="@dimen/dp200"
        zhy:testAttr="520"
        zhy:text="@string/hello_world" />

現(xiàn)在再次運行的結(jié)果是:

MyTextView(4692): attrName = layout_width , attrVal = @2131165234
MyTextView(4692): attrName = layout_height , attrVal = @2131165235
MyTextView(4692): attrName = text , attrVal = @2131361809
MyTextView(4692): attrName = testAttr , attrVal = 520
>>use typedarray
MyTextView(4692): text = Hello world! , textAttr = 520

發(fā)現(xiàn)了什么零抬?通過AttributeSet獲取的值镊讼,如果是引用都變成了@+數(shù)字的字符串。你說平夜,這玩意你能看懂么蝶棋?那么你看看最后一行使用TypedArray獲取的值,是不是瞬間明白了什么忽妒。

TypedArray其實是用來簡化我們的工作的玩裙,比如上例,如果布局中的屬性的值是引用類型(比如:@dimen/dp100)段直,如果使用AttributeSet去獲得最終的像素值吃溅,那么需要第一步拿到id,第二步再去解析id坷牛。而TypedArray正是幫我們簡化了這個過程罕偎。

貼一下:如果通過AttributeSet獲取最終的像素值的過程:

int widthDimensionId =  attrs.getAttributeResourceValue(0, -1);
        Log.e(TAG, "layout_width= "+getResources().getDimension(widthDimensionId));

現(xiàn)在別人問你TypedArray存在的意義很澄,你就可以告訴他了京闰。

4、declare-styleable###

我們已經(jīng)解決了兩個問題甩苛,接下來蹂楣,我們看看布局文件,我們有一個屬性叫做:zhy:text讯蒲。
總所周知痊土,系統(tǒng)提供了一個屬性叫做:Android:text,那么我覺得直接使用android:text更nice墨林,這樣的話赁酝,考慮問題:

如果系統(tǒng)中已經(jīng)有了語義比較明確的屬性犯祠,我可以直接使用嘛?

答案是可以的酌呆,怎么做呢衡载?
直接在attrs.xml中使用android:text屬性。

 <declare-styleable name="test">
        <attr name="android:text" />
        <attr name="testAttr" format="integer" />
    </declare-styleable>

注意隙袁,這里我們是使用已經(jīng)定義好的屬性痰娱,不需要去添加format屬性(注意聲明和使用的區(qū)別,差別就是有沒有format)菩收。
然后在類中這么獲壤嬲觥:ta.getString(R.styleable.test_android_text);布局文件中直接android:text="@string/hello_world"即可。

這里提一下娜饵,系統(tǒng)中定義的屬性坡贺,其實和我們自定義屬性的方式類似,你可以在sdk/platforms/android-xx/data/res/values該目錄下看到系統(tǒng)中定義的屬性箱舞。然后你可以在系統(tǒng)提供的View(eg:TextView)的構(gòu)造方法中發(fā)現(xiàn)TypedArray獲取屬性的代碼(自己去看一下)拴念。

接下來,我在想褐缠,既然declare-styleable這個標簽的name都能隨便寫政鼠,這么隨意的話,那么考慮問題:

styleable 的含義是什么队魏?可以不寫嘛公般?我自定義屬性,我聲明屬性就好了胡桨,為什么一定要寫個styleable呢官帘?

其實的確是可以不寫的,怎么做呢昧谊?

首先刪除declare-styleable的標簽
那么現(xiàn)在的attrs.xml為:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="testAttr" format="integer" />
</resources>

喲西刽虹,so清爽~
MyTextView實現(xiàn)

package com.example.test;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

public class MyTextView extends View {

    private static final String TAG = MyTextView.class.getSimpleName();

    private static final int[] mAttr = { android.R.attr.text, R.attr.testAttr };
    private static final int ATTR_ANDROID_TEXT = 0;
    private static final int ATTR_TESTATTR = 1;

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // ==>use typedarray
        TypedArray ta = context.obtainStyledAttributes(attrs, mAttr);

        String text = ta.getString(ATTR_ANDROID_TEXT);
        int textAttr = ta.getInteger(ATTR_TESTATTR, -1);
        //輸出 text = Hello world! , textAttr = 520
        Log.e(TAG, "text = " + text + " , textAttr = " + textAttr);

        ta.recycle();
    }

}

貌似多了些代碼,可以看到我們聲明了一個int數(shù)組呢诬,數(shù)組中的元素就是我們想要獲取的attr的id涌哲。并且我們根據(jù)元素的在數(shù)組中的位置,定義了一些整形的常量代表其下標尚镰,然后通過TypedArray進行獲取阀圾。
可以看到,我們原本的:

R.styleable.test => mAttr
R.styleable.test_text => ATTR_ANDROID_TEXT(0)
R.styleable.test_testAttr => ATTR_TESTATTR(1)

那么其實呢狗唉?android在其內(nèi)部也會這么做初烘,按照傳統(tǒng)的寫法,它會在R.Java生成如下代碼:

public static final class attr {
    public static final int testAttr=0x7f0100a9;
    }
public static final class styleable {
     public static final int test_android_text = 0;
     public static final int test_testAttr = 1;
      public static final int[] test = {
            0x0101014f, 0x7f0100a9
        };
    }

根據(jù)上述你應(yīng)該發(fā)現(xiàn)了什么。styleale的出現(xiàn)系統(tǒng)可以為我們完成很多常量(int[]數(shù)組肾筐,下標常量)等的編寫,簡化我們的開發(fā)工作(想想如果一堆屬性吗铐,自己編寫常量,你得寫成什么樣的代碼)抓歼。那么大家肯定還知道declare-styleable的name屬性,一般情況下寫的都是我們自定義View的類名谣妻。主要為了直觀的表達萄喳,該declare-styleable的屬性他巨,都是改View所用的。

現(xiàn)在5個問題减江,回答了4個,第一個問題:

 自定義屬性的幾個步驟是如何奏效的辈灼?

恩,上述以及基本涵蓋了這個問題的答案巡莹,大家自己總結(jié),所以:略降宅。

總結(jié)下今天的博客骂远。

  • attrs.xml里面的declare-styleable以及item激才,android會根據(jù)其在R.java中生成一些常量方便我們使用(aapt干的),本質(zhì)上额嘿,我們可以不聲明declare-styleable僅僅聲明所需的屬性即可。
  • 我們在View的構(gòu)造方法中岩睁,可以通過AttributeSet去獲得自定義屬性的值揣云,但是比較麻煩捕儒,而TypedArray可以很方便的便于我們?nèi)カ@取。
  • 我們在自定義View的時候阎毅,可以使用系統(tǒng)已經(jīng)定義的屬性。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末点弯,一起剝皮案震驚了整個濱河市扇调,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抢肛,老刑警劉巖狼钮,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捡絮,居然都是意外死亡熬芜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門福稳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涎拉,“玉大人,你說我怎么就攤上這事的圆」呐。” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵越妈,是天一觀的道長季俩。 經(jīng)常有香客問我,道長梅掠,這世上最難降的妖魔是什么种玛? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮瓤檐,結(jié)果婚禮上赂韵,老公的妹妹穿的比我還像新娘。我一直安慰自己挠蛉,他們只是感情好祭示,可當我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谴古,像睡著了一般质涛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上掰担,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天汇陆,我揣著相機與錄音,去河邊找鬼带饱。 笑死毡代,一個胖子當著我的面吹牛阅羹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播教寂,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼捏鱼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了酪耕?” 一聲冷哼從身側(cè)響起导梆,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎迂烁,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狡忙,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡灾茁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年北专,在試婚紗的時候發(fā)現(xiàn)自己被綠了旬陡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡驶睦,死狀恐怖场航,靈堂內(nèi)的尸體忽然破棺而出廉羔,到底是詐尸還是另有隱情,我是刑警寧澤孩饼,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布镀娶,位于F島的核電站揪罕,受9級特大地震影響宝泵,放射性物質(zhì)發(fā)生泄漏鲁猩。R本人自食惡果不足惜罢坝,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一嘁酿、第九天 我趴在偏房一處隱蔽的房頂上張望男应。 院中可真熱鬧,春花似錦沐飘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽影晓。三九已至,卻和暖如春疤祭,著一層夾襖步出監(jiān)牢的瞬間饵婆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工谓传, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留续挟,地道東北人侥衬。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓跑芳,卻偏偏與公主長得像直颅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子盆佣,可洞房花燭夜當晚...
    茶點故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容