參考文章天梧。
http://blog.csdn.net/xmxkf/article/details/51468648
http://blog.csdn.net/lmj623565791/article/details/45022631;
1.初始Custom View的構(gòu)造函數(shù)
通常我們在實現(xiàn)Custom View的時候,都會先繼承View并實現(xiàn)View的三個構(gòu)造函數(shù),例如:
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
public class MyCustomView extends View {
/**
* 第一個構(gòu)造函數(shù)
*/
public MyCustomView(Context context) {
this(context, null);
}
/**
* 第二個構(gòu)造函數(shù)
*/
public MyCustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* 第三個構(gòu)造函數(shù)
*/
public MyCustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO:獲取自定義屬性
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
- 在代碼中直接new一個Custom View實例的時候,會調(diào)用第一個構(gòu)造函數(shù).
- 在xml布局文件中調(diào)用Custom View的時候<CustomView/>,確切的說是xml解析到當前的標簽的時候,會調(diào)用第二個構(gòu)造函數(shù).
- 在xml布局文件中調(diào)用Custom View,并且Custom View標簽中還有自定義屬性時,這里調(diào)用的還是第二個構(gòu)造函數(shù).
也就是說,系統(tǒng)默認只會調(diào)用Custom View的前兩個構(gòu)造函數(shù),至于第三個構(gòu)造函數(shù)的調(diào)用,通常是我們自己在構(gòu)造函數(shù)中主動調(diào)用的(例如,在第二個構(gòu)造函數(shù)中調(diào)用第三個構(gòu)造函數(shù)).
接下來就針對自定義View的屬性展開:
2.為什么要自定義屬性
要使用屬性,首先這個屬性應(yīng)該存在,所以如果我們要使用自己的屬性满葛,必須要先把他定義出來才能使用。但我們平時在寫布局文件的時候好像沒有自己定義屬性罢屈,但我們照樣可以用很多屬性嘀韧,這是為什么?我想大家應(yīng)該都知道:系統(tǒng)定義好的屬性我們就可以拿來用唄缠捌,但是你們知道系統(tǒng)定義了哪些屬性嗎锄贷?哪些屬性是我們自定義控件可以直接使用的,哪些不能使用曼月?什么樣的屬性我們能使用谊却?這些問題我想大家不一定都弄得清除,下面我們?nèi)ヒ灰唤忾_這些謎團哑芹。
??系統(tǒng)定義的所有屬性我們可以在\sdk\platforms\android-xx\data\res\values目錄下找到attrs.xml這個文件炎辨,這就是系統(tǒng)自帶的所有屬性,打開看看一些比較熟悉的:
<declare-styleable name="View">
<attr name="id" format="reference" />
<attr name="background" format="reference|color" />
<attr name="padding" format="dimension" />
...
<attr name="focusable" format="boolean" />
...
</declare-styleable>
<declare-styleable name="TextView">
<attr name="text" format="string" localization="suggested" />
<attr name="hint" format="string" />
<attr name="textColor" />
<attr name="textColorHighlight" />
<attr name="textColorHint" />
...
</declare-styleable>
<declare-styleable name="ViewGroup_Layout">
<attr name="layout_width" format="dimension">
<enum name="fill_parent" value="-1" />
<enum name="match_parent" value="-1" />
<enum name="wrap_content" value="-2" />
</attr>
<attr name="layout_height" format="dimension">
<enum name="fill_parent" value="-1" />
<enum name="match_parent" value="-1" />
<enum name="wrap_content" value="-2" />
</attr>
</declare-styleable>
<declare-styleable name="LinearLayout_Layout">
<attr name="layout_width" />
<attr name="layout_height" />
<attr name="layout_weight" format="float" />
<attr name="layout_gravity" />
</declare-styleable>
<declare-styleable name="RelativeLayout_Layout">
<attr name="layout_centerInParent" format="boolean" />
<attr name="layout_centerHorizontal" format="boolean" />
<attr name="layout_centerVertical" format="boolean" />
...
</declare-styleable>
看看上面attrs.xml文件中的屬性聪姿,發(fā)現(xiàn)他們都是有規(guī)律的分組的形式組織的碴萧。以declare-styleable 為一個組合,后面有一個name屬性咳燕,屬性的值為View 勿决、TextView 等等,有沒有想到什么招盲?沒錯,屬性值為View的那一組就是為View定義的屬性嘉冒,屬性值為TextView的就是為TextView定義的屬性…曹货。
因為所有的控件都是View的子類咆繁,所以為View定義的屬性所有的控件都能使用,這就是為什么我們的自定義控件沒有定義屬性就能使用一些系統(tǒng)屬性顶籽。
但是并不是每個控件都能使用所有屬性玩般,比如TextView是View的子類,所以為View定義的所有屬性它都能使用礼饱,但是子類肯定有自己特有的屬性坏为,得單獨為它擴展一些屬性,而單獨擴展的這些屬性只有它自己能有镊绪,View是不能使用的匀伏,比如View中不能使用android:text=“”。又比如蝴韭,LinearLayout中能使用layout_weight屬性够颠,而RelativeLayout卻不能使用,因為layout_weight是為LinearLayout的LayoutParams定義的榄鉴。
綜上所述履磨,自定義控件如果不自定義屬性,就只能使用VIew的屬性庆尘,但為了給我們的控件擴展一些屬性剃诅,我們就必須自己去定義。
2. 怎樣自定義屬性
自定義屬性的步驟想必大家都所了解:
1驶忌、自定義一個CustomView(extends View )類
2矛辕、編寫values/attrs.xml,在其中編寫styleable和item等標簽元素
3位岔、在布局文件中CustomView使用自定義的屬性(注意命名空間)
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是什么鬼纠屋?從哪冒出來的涂臣,就要我去使用?
看完本篇文章,相信你會徹底解決上述問題赁遗。
翻閱系統(tǒng)的屬性文件署辉,你會發(fā)現(xiàn),有兩種獲取形式(下面講)岩四;這兩種
區(qū)別就是attr標簽后面帶不帶format屬性哭尝,如果帶format的就是在定義屬性,如果不帶format的就是在使用已有的屬性剖煌,name的值就是屬性的名字材鹦,format是限定當前定義的屬性能接受什么值。
打個比方耕姊,比如系統(tǒng)已經(jīng)定義了android:text屬性桶唐,我們的自定義控件也需要一個文本的屬性,可以有兩種方式:
第一種:我們并不知道系統(tǒng)定義了此名稱的屬性箩做,我們自己定義一個名為text或者mText的屬性(屬性名稱可以隨便起的)
<resources>
<declare-styleable name="MyTextView">
<attr name=“text" format="string" />
</declare-styleable>
</resources>
第二種:我們知道系統(tǒng)已經(jīng)定義過名稱為text的屬性莽红,我們不用自己定義,只需要在自定義屬性中申明邦邦,我要使用這個text屬性
(注意加上android命名空間安吁,這樣才知道使用的是系統(tǒng)的text屬性)
<resources>
<declare-styleable name="MyTextView">
<attr name=“android:text"/>
</declare-styleable>
</resources>
為什么系統(tǒng)定義了此屬性,我們在使用的時候還要聲明燃辖?因為鬼店,系統(tǒng)定義的text屬性是給TextView使用的,如果我們不申明黔龟,就不能使用text屬性妇智。
4. 屬性值的類型format
format支持的類型一共有11種:
(1). reference:參考某一資源ID
- 屬性定義:
<declare-styleable name = "名稱">
<attr name = "background" format = "reference" />
</declare-styleable>
- 屬性使用:
<ImageView android:background = "@drawable/圖片ID"/>
(2). color:顏色值
- 屬性定義:
<attr name = "textColor" format = "color" />
- 屬性使用:
<TextView android:textColor = "#00FF00" />
(3). boolean:布爾值
- 屬性定義:
<attr name = "focusable" format = "boolean" />
- 屬性使用:
<Button android:focusable = "true"/>
(1). reference:參考某一資源ID
- 屬性定義:
<declare-styleable name = "名稱">
<attr name = "background" format = "reference" />
</declare-styleable>
- 屬性使用:
<ImageView android:background = "@drawable/圖片ID"/>
(4). dimension:尺寸值
- 屬性定義:
<attr name = "layout_width" format = "dimension" />
- 屬性使用:
<Button android:layout_width = "42dip"/>
(5). float:浮點值
- 屬性定義:
<attr name = "fromAlpha" format = "float" />
- 屬性使用:
<alpha android:fromAlpha = "1.0"/>
(6). integer:整型值
- 屬性定義:
<attr name = "framesCount" format="integer" />
- 屬性使用:
<animated-rotate android:framesCount = "12"/>
(7). string:字符串
- 屬性定義:
<attr name = "text" format = "string" />
- 屬性使用:
<TextView android:text = "我是文本"/>
(8). fraction:百分數(shù)
- 屬性定義:
<attr name = "pivotX" format = "fraction" />
- 屬性使用:
<rotate android:pivotX = "200%"/>
(9). enum:枚舉值
- 屬性定義:
<declare-styleable name="名稱">
<attr name="orientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
</declare-styleable>
- 屬性使用:
<LinearLayout
android:orientation = "vertical">
</LinearLayout>
注意:枚舉類型的屬性在使用的過程中只能同時使用其中一個,不能 android:orientation = “horizontal|vertical"
(10). flag:位或運算
- 屬性定義:
<declare-styleable name="名稱">
<attr name="gravity">
<flag name="top" value="0x30" />
<flag name="bottom" value="0x50" />
<flag name="left" value="0x03" />
<flag name="right" value="0x05" />
<flag name="center_vertical" value="0x10" />
...
</attr>
</declare-styleable>
- 屬性使用:
<TextView android:gravity="bottom|left"/>
注意:位運算類型的屬性在使用的過程中可以使用多個值
(11). 混合類型:屬性定義時可以指定多種類型值
- 屬性定義:
<declare-styleable name = "名稱">
<attr name = "background" format = "reference|color" />
</declare-styleable>
- 屬性使用:
<ImageView
android:background = "@drawable/圖片ID" />
或者:
<ImageView
android:background = "#00FF00" />
通過上面的學(xué)習(xí)我們已經(jīng)知道怎么定義各種類型的屬性氏身,以及怎么使用它們巍棱,但是我們寫好布局文件之后,要在控件中使用這些屬性還需要將它解析出來蛋欣。
5. 類中獲取屬性值
在這之前航徙,順帶講一下命名空間,我們在布局文件中使用屬性的時候(android:layout_width="match_parent"
)發(fā)現(xiàn)前面都帶有一個android:陷虎,這個android就是上面引入的命名空間xmlns:android="http://schemas.android.com/apk/res/android”
到踏,表示到android系統(tǒng)中查找該屬性來源。只有引入了命名空間尚猿,XML文件才知道下面使用的屬性應(yīng)該去哪里找(哪里定義的窝稿,不能憑空出現(xiàn),要有根據(jù))凿掂。
如果我們自定義屬性伴榔,這個屬性應(yīng)該去我們的應(yīng)用程序包中找,所以要引入我們應(yīng)用包的命名空間xmlns:itydl="http://schemas.android.com/apk/res-auto”
,res-auto表示自動查找潮梯,還有一種寫法xmlns:itydl="http://schemas.android.com/apk/com.example.openxu.
myview"骗灶,com.example.itydl.myview為我們的應(yīng)用程序包名惨恭。
按照上面學(xué)習(xí)的知識秉馏,我們先定義一些屬性,并寫好布局文件脱羡。
先在res\values目錄下創(chuàng)建attrs.xml萝究,定義自己的屬性:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyTextView">
<!--聲明MyTextView需要使用系統(tǒng)定義過的text屬性,注意前面需要加上android命名-->
<attr name="android:text" />
<attr name="mTextColor" format="color" />
<attr name="mTextSize" format="dimension" />
</declare-styleable>
</resources>
注意,上面styleable的name寫的是MyTextView锉罐,這個其實不一定要寫自定義View的類名帆竹,其他的字母也是可以的。只不過規(guī)范化書寫一般會寫上針對哪個類的類名稱
在布局文件中脓规,使用屬性(注意引入我們應(yīng)用程序的命名空間栽连,這樣在能找到我們包中的attrs):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:itydl="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.itydl.myview.MyTextView
android:layout_width="200dip"
android:layout_height="100dip"
itydl:mTextSize="25sp"
android:text="我是文字"
itydl:mTextColor ="#0000ff"
android:background="#ff0000"/>
</LinearLayout>
在構(gòu)造方法中獲取屬性值:
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyTextView);
String text = ta.getString(R.styleable.MyTextView_android_text);
int mTextColor = ta.getColor(R.styleable.MyTextView_mTextColor, Color.BLACK);
int mTextSize = ta.getDimensionPixelSize(R.styleable.MyTextView_mTextSize, 100);
ta.recycle(); //注意回收
Log.v("itydl", “text屬性值:"+mText);
Log.v("itydl", "mTextColor屬性值:"+mTextColor);
Log.v("itydl", "mTextSize屬性值:"+mTextSize);
}
log輸出:
6. Attributeset和TypedArray以及declare-styleable
Attributeset看名字就知道是一個屬性的集合,實際上侨舆,它內(nèi)部就是一個XML解析器秒紧,幫我們將布局文件中該控件的所有屬性解析出來,并以key-value的兼職對形式維護起來挨下。其實我們完全可以只用他通過下面的代碼來獲取我們的屬性就行熔恢。
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
int count = attrs.getAttributeCount();
for (int i = 0; i < count; i++) {
String attrName = attrs.getAttributeName(i);
String attrVal = attrs.getAttributeValue(i);
Log.e("openxu", "attrName = " + attrName + " , attrVal = " + attrVal);
}
}
log輸出:
發(fā)現(xiàn)通過Attributeset獲取屬性的值時,它將我們布局文件中的值原原本本的獲取出來的臭笆,比如寬度200.0dip叙淌,其實這并不是我們想要的,如果我們接下來要使用寬度值愁铺,我們還需要將dip去掉鹰霍,然后轉(zhuǎn)換成整形,這多麻煩茵乱。其實這都不算什么茂洒,更惡心的是,backgroud我應(yīng)用了一個color資源ID似将,它直接給我拿到了這個ID值获黔,前面還加了個@,接下來我要自己獲取資源在验,并通過這個ID值獲取到真正的顏色玷氏。
我們再換TypedArray試試。
使用TypedArray獲取屬性值:
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyTextView);
String mText = ta.getString(R.styleable.MyTextView_android_text);
int mTextColor = ta.getColor(R.styleable.MyTextView_mTextColor, Color.BLACK);
int mTextSize = ta.getDimensionPixelSize(R.styleable.MyTextView_mTextSize, 100);
float width = ta.getDimension(R.styleable.MyTextView_android_layout_width, 0.0f);
float hight = ta.getDimension(R.styleable.MyTextView_android_layout_height,0.0f);
int backgroud = ta.getColor(R.styleable.MyTextView_android_background, Color.BLACK);
ta.recycle(); //注意回收
Log.v("itydl", "width:"+width);
Log.v("itydl", "hight:"+hight);
Log.v("itydl", "backgroud:"+backgroud);
Log.v("itydl", "mText:"+mText);
Log.v("itydl", "mTextColor:"+mTextColor);
Log.v("itydl", "mTextSize:"+mTextSize);ext, 0, mText.length(), mBound);
}
log輸出:
看看多么舒服的結(jié)果腋舌,我們得到了想要的寬高(float型)盏触,背景顏色(color的十進制)等,TypedArray提供了一系列獲取不同類型屬性的方法,這樣就可以直接得到我們想要的數(shù)據(jù)類型赞辩,而不用像Attributeset獲取屬性后還要一個個處理才能得到具體的數(shù)據(jù)雌芽,實際上TypedArray是為我們獲取屬性值提供了方便,注意一點辨嗽,TypedArray使用完畢后記得調(diào)用 ta.recycle();回收 世落。
7、快速獲取各屬性的方式:
你可能還見過如下方式自定義屬性的文章糟需,其實接下來要說的就是一種快速獲取自定義屬性的方式:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="titleText" format="string" />
<attr name="titleTextColor" format="color" />
<attr name="titleTextSize" format="dimension" />
<declare-styleable name="CustomTitleView">
<attr name="titleText" />
<attr name="titleTextColor" />
<attr name="titleTextSize" />
</declare-styleable>
</resources>
然后在布局中聲明我們的自定義View
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.customview01.view.CustomTitleView
android:layout_width="200dp"
android:layout_height="100dp"
custom:titleText="3712"
custom:titleTextColor="#ff0000"
custom:titleTextSize="40sp" />
</RelativeLayout>
在View的構(gòu)造方法中屉佳,獲得我們的自定義的樣式
/**
* 獲得我們所定義的自定義樣式屬性
*/
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyle, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++)
{
int attr = a.getIndex(i);
switch (attr)
{
case R.styleable.CustomTitleView_titleText:
mTitleText = a.getString(attr);
break;
case R.styleable.CustomTitleView_titleTextColor:
// 默認顏色設(shè)置為黑色
mTitleTextColor = a.getColor(attr, Color.BLACK);
break;
case R.styleable.CustomTitleView_titleTextSize:
// 默認設(shè)置為16sp,TypeValue也可以把sp轉(zhuǎn)化為px
mTitleTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;
}
}
a.recycle();
這種方式在實際開發(fā)中也是最常見的方式洲押。
8.declare-styleable
他是用來干嘛的武花,如果不要它可不可以?
直接給答案吧杈帐。答案是可以的体箕,我們自定義屬性完全可以寫成下面的形式:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="mTextColor" format="color" />
<attr name="mTextSize" format="dimension" />
</resources>
之前的形式是這樣的:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyTextView">
<attr name="android:text" />
<attr name="android:layout_width" />
<attr name="android:layout_height" />
<attr name="android:background" />
<attr name="mTextColor" format="color" />
<attr name="mTextSize" format="dimension" />
</declare-styleable>
</resources>
或者:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--定義屬性-->
<attr name="mTextColor" format="color" />
<attr name="mTextSize" format="dimension" />
<declare-styleable name="MyTextView">
<!--生成索引-->
<attr name="android:text" />
<attr name="android:layout_width" />
<attr name="android:layout_height" />
<attr name="android:background" />
<attr name=“mTextColor" />
<attr name="mTextSize" />
</declare-styleable>
</resources>
我們都知道所有的資源文件在R中都會對應(yīng)一個整型常亮,我們可以通過這個ID值找到資源文件挑童。
??屬性在R中對應(yīng)的類是public static final class attr
累铅,如果我們寫了declare-styleable
,在R文件中就會生成styleable類炮沐,這個類其實就是將每個控件的屬性分組争群,然后記錄屬性的索引值,而TypedArray正好需要通過此索引值獲取屬性大年。
public static final class styleable
public static final int[] MyTextView = {
0x0101014f, 0x7f010038, 0x7f010039
};
public static final int MyTextView_android_text = 0;
public static final int MyTextView_mTextColor = 1;
public static final int MyTextView_mTextSize = 2;
}
記住了嗎换薄?如果不要它也是可以噠。
相信看完文章翔试,自定義屬性相關(guān)的知識已經(jīng)難不住你了~當然這也是自定義View必須掌握的基礎(chǔ)知識轻要。