自定義View分類
-
繼承自View
通常用于實(shí)現(xiàn)一些不規(guī)則的效果欢顷,這些效果不方便或者不能夠用布局組合的方式實(shí)現(xiàn)铺峭。這種自定義的View需要通過自定義繪制方式來實(shí)現(xiàn)墓怀,即重寫onDraw方法,需要注意的是卫键,注意wrap_content和padding兩個(gè)屬性的處理傀履。不做處理的話會(huì)導(dǎo)致這兩個(gè)參數(shù)失效 -
繼承自ViewGroup
主要用于實(shí)現(xiàn)自定義的布局,比如將幾個(gè)不同的View合并起來作為一個(gè)新的常用的Layout莉炉,使用的時(shí)候直接引入就會(huì)方便許多钓账。需要注意的是碴犬,ViewGroup和各個(gè)子元素的的Measure,Layout過程一定要處理好梆暮。 -
繼承自特定的View
這種方法比較常見服协,主要用于對(duì)現(xiàn)有View進(jìn)行擴(kuò)展,實(shí)現(xiàn)自己需要的特殊功能啦粹,而且實(shí)現(xiàn)起來相對(duì)簡(jiǎn)單偿荷,wrap_content和padding也不需要手動(dòng)處理 -
繼承自特定ViewGroup
這種方法也比較常見,而且不需要手動(dòng)處理ViewGroup的Measure唠椭,Layout過程跳纳,通常來說,繼承自ViewGroup能夠?qū)崿F(xiàn)的布局采用這種方式都能實(shí)現(xiàn)贪嫂,只不過直接繼承自ViewGroup更接近底層棒旗,靈活性也更好。
自定義View流程及常用方法解析
1.構(gòu)造函數(shù)
View的構(gòu)造函數(shù)有四種重載:
public void MyView(Context context) {}
public void MyView(Context context, AttributeSet attrs) {}
public void MyView(Context context, AttributeSet attrs, int defStyleAttr) {}
public void MyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {}
常用到的只有第一第二兩種撩荣。第一個(gè)只有Context參數(shù)铣揉,在代碼中創(chuàng)建View時(shí),會(huì)調(diào)用此方法餐曹。
第二個(gè)有兩個(gè)參數(shù)逛拱,Context和AttributeSet,用在XML中創(chuàng)建View台猴,AttributeSet用來保存XML文件中View的各項(xiàng)屬性朽合。
后兩個(gè)參數(shù)的含義:
defStyleAttr參數(shù):這是一個(gè)定義在attrs.xml文件中的attribute。這個(gè)值起作用需要兩個(gè)條件:1. 值不為0饱狂;2. 在Theme中使用了(出現(xiàn)即可)曹步。
defStyleRes參數(shù): 這是在styles.xml文件中定義的一個(gè)style。只有當(dāng)defStyleAttr沒有起作用休讳,才會(huì)使用到這個(gè)值讲婚。
在分析這兩個(gè)參數(shù)的含義之前,我們先來了解一下View的屬性是怎樣設(shè)置的.
View的Style和Theme
首先俊柔,在XML文件中為View指定屬性有以下幾種方式:
- 直接在layout中設(shè)置
- 設(shè)置style筹麸,并在layout中指定Style
- Application和Activity可以指定theme,在theme中設(shè)置當(dāng)前Application或者Activity的屬性默認(rèn)值雏婶。
<EditText
android:id="@+id/cusview_attrs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
style="@style/MyTextStyle"
android:text="simple image view"
/>
這是一個(gè)EditText物赶,我們?cè)趌ayout文件中設(shè)置了他的基本屬性,android:textSize="24sp"留晚,這個(gè)很容易理解酵紫。除此之外還有一個(gè)style="@style/MyTextStyle",這個(gè)Style是在values/style.xml文件中定義的:
<style name="MyTextStyle" >
<item name="android:textColor" >#00FF00</item>
</style>
指定了一個(gè)顏色為綠色,可以看一下效果奖地,最終呈現(xiàn)出來的屬性就是24sp+綠色状蜗。
那么又有新的問題了,如果同時(shí)在屬性和Style中對(duì)同一屬性設(shè)置不同的值鹉动,結(jié)果會(huì)是怎樣的轧坎? 大家可以試一下,layout中直接指定的屬性優(yōu)先級(jí)高于Style泽示。
關(guān)于Style缸血,他還有一個(gè)parent父屬性,指定當(dāng)前Style是繼承自哪一個(gè)Style的:
<style name="MyTextStyle" parent="@android:style/TextAppearence">
<item name="android:textColor" >#00FF00</item>
</style>
這表示我們自定義的MyTextStyle繼承了android:style/TextAppearence的默認(rèn)屬性械筛,同時(shí)指定textColor覆寫了父屬性捎泻。
介紹完了Style,現(xiàn)在我們?cè)賮砜匆幌耇heme是怎樣使用的埋哟。Style通常是針對(duì)某一個(gè)View進(jìn)行設(shè)置的笆豁,而theme針對(duì)的是Application和Activity。打開manifest文件赤赊,我們會(huì)看到以下的內(nèi)容:
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
......
Application會(huì)指定一個(gè)theme屬性闯狱,他的值在style中定義了:
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
</style>
是完全繼承自AppBaseTheme的。就是說抛计,這個(gè)應(yīng)用的主題就是AppBaseTheme哄孤。當(dāng)然我們也可以自定義theme。
<style name="MyAppTheme" parent="AppBaseTheme">
<item name="android:background">#00FF00</item>
</style>
這里設(shè)置了一個(gè)自定義的主題吹截,背景色為綠色瘦陈,將它應(yīng)用的某一個(gè)Activity:
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboardHidden|screenSize"
android:theme="@style/MyAppTheme">
......
然后再運(yùn)行一下,看看效果你就會(huì)發(fā)現(xiàn)波俄,被指定的Activity整個(gè)都綠了晨逝。。懦铺。捉貌。
Android系統(tǒng)的theme.xml和style.xml存放位置在sdk\platforms\android-x\data\res\values下,感興趣的可以自己查看阀趴。
構(gòu)造函數(shù)的參數(shù)分析
我們看一下View的構(gòu)造函數(shù)源碼:
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
View的各項(xiàng)屬性是context.obtainStyledAttributes方法來獲取的昏翰,他包含四個(gè)參數(shù)
第一苍匆,三刘急,四個(gè)參數(shù)很明顯,是從構(gòu)造函數(shù)里傳入的浸踩,第二個(gè)參數(shù)com.android.internal.R.styleable.View叔汁,表名了我們將要獲取的屬性。我們用一個(gè)例子來解釋,順便學(xué)習(xí)一下自定義參數(shù)的使用据块。
第一步码邻,在values下新建一個(gè)attrs.xml文件,這里定義好所需要的參數(shù)名稱和格式:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomizeView">
<attr name="attr_1" format="string"/>
<attr name="attr_2" format="string"/>
<attr name="attr_3" format="string"/>
<attr name="attr_4" format="string"/>
<attr name="attr_5" format="string"/>
</declare-styleable>
<attr name="CustomizeStyle" format="reference"/>
</resources>
這里我們定義了一個(gè)名稱為 MyCustomizeView 的styleable另假,他包含四項(xiàng)屬性 attr_1像屋,2 , 3, 4, 5,另外我們還定義了一個(gè)屬性CustomizeStyle边篮,他的類型是reference己莺,表示它可以接收一個(gè)style引用,各項(xiàng)屬性的值戈轿,可以在這個(gè)引用中定義凌受。
將屬性定義在styleable 中,和直接定義沒有很大的區(qū)別思杯,不管定義在哪個(gè)位置胜蛉,這些attribute都會(huì)生效,只是定義在styleable 中的styleable 色乾,系統(tǒng)會(huì)在R.styleable中生成相關(guān)屬性誊册。我們可以通過R.styleable.MyCustomizeView來引用他。 直接定義的暖璧,需要通過R.attr.CustomizeStyle來引用解虱。
各項(xiàng)屬性的使用方法如下:
在layout文件中使用:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:myattrs="http://schemas.android.com/apk/com.training"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.training.view.MyCustomView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
myattrs:attr_1="Attr 1 set in layout "
style="@style/MyStyle"
/>
</LinearLayout>
注意,要使用自定義屬性漆撞,必須新建一個(gè)自定義屬性的命名空間:
xmlns:名字="http://schemas.android.com/apk/res/包名"
或者xmlns:名字="http://schemas.android.com/apk/res-auto"
我們?cè)趯傩灾惺褂昧藄tyle殴泰,所以也要在style.xml中定義它:
<style name="MyStyle">
<item name="attr_1">attr 1 set in style </item>
<item name="attr_2">attr 2 set in style </item>
</style>
現(xiàn)在我們回到構(gòu)造函數(shù),
public void MyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {}
后邊兩個(gè)參數(shù)怎樣使用呢浮驳? 開始的部分我們提到了:
defStyleAttr參數(shù):這是一個(gè)定義在attrs.xml文件中的attribute悍汛。這個(gè)值起作用需要兩個(gè)條件:1. 值不為0;2. 在Theme中使用了(出現(xiàn)即可)至会。
defStyleRes參數(shù): 這是在styles.xml文件中定義的一個(gè)style离咐。只有當(dāng)defStyleAttr沒有起作用,才會(huì)使用到這個(gè)值奉件。
defStyleAttr宵蛀,是定義在attrs.xml中的,在attrs中我們已經(jīng)定義了:
<attr name="CustomizeStyle" format="reference"/>
還要在Theme使用它县貌。代碼如下:
<style name="AppTheme" parent="AppBaseTheme">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
<item name="attr_1">attr 1 set in theme</item>
<item name="attr_2">attr 2 set in theme</item>
<item name="attr_3">attr 3 set in theme</item>
<item name="CustomizeStyle">@style/StyleInTheme</item>
</style>
<style name="StyleInTheme">
<item name="attr_1">attr 1 set in theme reference</item>
<item name="attr_2">attr 2 set in theme reference</item>
<item name="attr_3">attr 3 set in theme reference</item>
<item name="attr_4">attr 4 set in theme reference</item>
</style>
除此之外术陶,我們?cè)俣x一個(gè)style,對(duì)應(yīng)defStyleRes參數(shù)煤痕。
<style name="DefaultCustomizeStyle">
<item name="attr_1">attr 1 set in default style res</item>
<item name="attr_2">attr 2 set in default style res</item>
<item name="attr_3">attr 3 set in default style res</item>
<item name="attr_4">attr 4 set in default style res</item>
<item name="attr_5">attr 5 set in default style res</item>
</style>
現(xiàn)在準(zhǔn)備工作已經(jīng)結(jié)束梧宫,我們做了以下工作:
1:定義了5個(gè)自定義屬性接谨,attr_1,2塘匣,3脓豪,4,它們接收String值忌卤,還有一個(gè)CustomizeStyle屬性扫夜,接收一個(gè)reference
2:在layout文件中直接給attr_1賦值了,另外還設(shè)置了一個(gè)MyStyle驰徊,在style中給attr_1历谍,2都進(jìn)行了賦值
3:在Application使用的theme中,直接對(duì)attr_1,2,3進(jìn)行賦值辣垒,同時(shí)設(shè)置了CustomizeStyle的引用對(duì)象望侈,在引用中對(duì)attr_1,2,3,4賦值。
4:設(shè)置了一個(gè)DefaultCustomizeStyle勋桶,對(duì)attr_1,2,3,4,5都進(jìn)行賦值脱衙。
現(xiàn)在我們看一下怎樣使用這些參數(shù):
public class MyCustomView extends View{
private final String TAG = "ViewConstructor";
public MyCustomView(Context context) {
super(context);
}
public MyCustomView(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.CustomizeStyle);
}
public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, R.style.DefaultCustomizeStyle):
}
public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomizeView, defStyleAttr, defStyleRes);
String one = a.getString(R.styleable.MyCustomizeView_attr_1);
String two = a.getString(R.styleable.MyCustomizeView_attr_2);
String three = a.getString(R.styleable.MyCustomizeView_attr_3);
String four = a.getString(R.styleable.MyCustomizeView_attr_4);
String five = a.getString(R.styleable.MyCustomizeView_attr_5);
Log.d(TAG, "ATTR_1------>" + one);
Log.d(TAG, "ATTR_2------>" + two);
Log.d(TAG, "ATTR_3------>" + three);
Log.d(TAG, "ATTR_4------>" + four);
Log.d(TAG, "ATTR_5------>" + five);
a.recycle();
}
現(xiàn)在運(yùn)行一下,就可以看到結(jié)果了:
ATTR_1------>Attr 1 set in layout
ATTR_2------>attr 2 set in style
ATTR_3------>attr 3 set in theme reference
ATTR_4------>attr 4 set in theme reference
ATTR_5------>null
attr_1在layout例驹,style捐韩,theme,theme的屬性引用鹃锈,defaultStyle中都賦值了荤胁,結(jié)果是layout中的賦值
attr_2在style,theme屎债,theme的屬性引用仅政,defaultStyle中賦值了,結(jié)果是style中的值
attr_3在theme盆驹,theme的屬性引用圆丹,defaultStyle中賦值了,結(jié)果是theme的屬性引用中的值
attr_4在theme的屬性引用躯喇,defaultStyle中賦值了辫封,結(jié)果是theme的屬性引用中的值
attr_5在defaultStyle中賦值了,結(jié)果是null廉丽。
也就是說倦微,他們的優(yōu)先級(jí)是這樣的:
直接在layout中定義>在style定義>defStyleAttr(在theme中,對(duì)屬性的引用賦值正压,對(duì)應(yīng)第三個(gè)參數(shù))>defStyleRes(設(shè)置默認(rèn)的style欣福,對(duì)應(yīng)第四個(gè)參數(shù))。
同時(shí)也印證了上文提到的蔑匣,defStyleRes參數(shù)只有當(dāng)defStyleAttr沒有起作用劣欢,才會(huì)使用到這個(gè)值棕诵。
如果將defStyleAttr設(shè)置為0:
public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, 0, R.style.DefaultCustomizeStyle);
}
再運(yùn)行一下裁良,結(jié)果如下:
ATTR_1------>Attr 1 set in layout
ATTR_2------>attr 2 set in style
ATTR_3------>attr 3 set in default style res
ATTR_4------>attr 4 set in default style res
ATTR_5------>attr 5 set in default style res
這時(shí)候凿将,第四個(gè)參數(shù),defStyleRes終于起作用了价脾。
關(guān)于自定義參數(shù)的詳細(xì)說明牧抵,可以參考這位同學(xué)的文章