本來想寫一篇關(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)定義的屬性。