Android中View自定義XML屬性詳解以及R.attr與R.styleable的區(qū)別
Android中的各種Widget都提供了很多XML屬性,我們可以利用這些XML屬性在layout文件中為Widget的屬性賦值。
如下所示:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
我們可以通過TextView所提供的XML屬性android:text為TextView的文本賦值。
我們?cè)谧远xView的時(shí)候也會(huì)經(jīng)常需要自定義View的XML屬性。
假設(shè)我們有一個(gè)自定義的View,其類名是com.ispring.attr.MyView,其中com.ispring.attr是應(yīng)用程序的包名。
要想自定義XML屬性,總的來說包括三步:
- 在xml資源文件中定義各種
attr,指定attr的數(shù)據(jù)類型。 - 在自定義View的構(gòu)造函數(shù)中解析這些從XML中定義的屬性值,將其存放到View對(duì)應(yīng)的成員變量中。
- 在layout文件中為自定義View的XML屬性賦值。
首先,我們?cè)趓es/values目錄下新建了一個(gè)名為attrs_my_views.xml文件,文件名是什么不重要,只要是xml文件就行。我們?cè)谠撐募卸xMyView所支持的XML屬性。該文件的根結(jié)點(diǎn)是<resources>,我們?cè)?code><resources>節(jié)點(diǎn)下可以添加多個(gè)<attr>節(jié)點(diǎn),在<attr>節(jié)點(diǎn)中通過name指定XML屬性名稱,通過format指定XML屬性值的類型,如下圖所示:
由上圖我們可知,format支持的類型有enum、boolean、color、dimension、flag、float、fraction、integer、reference、string。
當(dāng)我們指定了XML屬性的名稱和屬性值的類型之后,我們就可以在layout文件中通過XML屬性為其賦值了。如下圖所示:
我們通過<com.ispring.attr.MyView>在layout中引入了MyView,為了能夠使用自定義屬性,我們通常要指定一個(gè)自定義的命名空間以區(qū)別于Android的命名空間xmlns:android,我們自定義命名空間的名字可以是任意的,通常我一般用xmlns:app。我的App的命名空間是com.ispring.attr,如果用Eclipse開發(fā),那么可以這樣定義命名空間xmlns:app=http://schemas.android.com/apk/res/com.ispring.attr,但是在Android Studio中這樣定義命名空間會(huì)有問題。Android Studio使用Gradle進(jìn)行build,而Gradle不允許自定義的命名空間以包名結(jié)尾,在Android Studio中可以這樣定義命名空間xmlns:app="http://schemas.android.com/apk/res-auto",這樣定義的命名空間自動(dòng)指向當(dāng)前App的命名空間。
在正確定義app的命名空間之后,我們就可以用app:customAttr為MyView的customAttr屬性賦值了。如果我們將customAttr的format定義為boolean的,那么此處就只能填寫true或者false,填寫其他類型的值會(huì)報(bào)錯(cuò)。
下面再對(duì)attr的format的類型進(jìn)行一下說明。
-
boolean
boolean表示attr是布爾類型的值,取值只能是true或false。
-
string
string表示attr是字符串類型。
-
integer
integer表示attr是整數(shù)類型,取值只能是整數(shù),不能是浮點(diǎn)數(shù)。
-
float
float表示attr是浮點(diǎn)數(shù)類型,取值只能是浮點(diǎn)數(shù)或整數(shù)。
-
fraction
fraction表示attr是百分?jǐn)?shù)類型,取值只能以%結(jié)尾,例如30%、120.5%等。
-
color
color表示attr是顏色類型,例如#ff0000,也可以使用一個(gè)指向Color的資源,比如@android:color/background_dark,但是不能用0xffff0000這樣的值。
-
dimension
dimension表示attr是尺寸類型,例如取值16px、16dp,也可以使用一個(gè)指向
<dimen>類型的資源,比如@android:dimen/app_icon_size。 -
reference
reference表示attr的值只能指向某一資源的ID,例如取值
@id/textView。 -
enum
enum表示attr是枚舉類型,在定義enum類型的attr時(shí),可以將attr的format設(shè)置為enum,也可以不用設(shè)置attr的format屬性,但是必須在attr節(jié)點(diǎn)下面添加一個(gè)或多個(gè)enum節(jié)點(diǎn)。如下所示:
<attr name="customAttr"> <enum name="man" value="0" /> <enum name="woman" value="1" /> </attr>這樣attr的屬性值只能取man或woman了。
-
flag
flag表示attr是bit位標(biāo)記,flag與enum有相似之處,定義了flag的attr,在設(shè)置值時(shí),可以通過
|設(shè)置多個(gè)值,而且每個(gè)值都對(duì)應(yīng)一個(gè)bit位,這樣通過按位或操作符|可以將多個(gè)值合成一個(gè)值,我們一般在用flag表示某個(gè)字段支持多個(gè)特性,需要注意的是,要想使用flag類型,不能在attr上設(shè)置format為flag,不要設(shè)置attr的format的屬性,直接在attr節(jié)點(diǎn)下面添加flag節(jié)點(diǎn)即可。如下所示:<attr name="customAttr"> <flag name="none" value="0" /> <flag name="bold" value="0x1" /> <flag name="italic" value="0x2" /> <flag name="underline" value="0x4" /> </attr>在
<attr>節(jié)點(diǎn)下通過定義多個(gè)<flag>表示其支持的值,value的值一般是0或者是2的N次方(N為大于等于0的整數(shù)),對(duì)于上面的例子我們?cè)趯?shí)際設(shè)置值是可以設(shè)置單獨(dú)的值,如none、bold、italic、underline,也可以通過|設(shè)置多個(gè)值,例如app:customAttr="italic|underline"。MyView直接繼承自View,我想讓MyView可以顯示文本,即我傳遞文本給MyView,MyView能畫出來,就相當(dāng)于非常簡(jiǎn)單的TextView。
因此,我的attrs_my_view.xml如下所示:
<resources>
<attr name="customText" format="string" />
<attr name="customColor" format="color" />
</resources>
我們定義了兩個(gè)XML屬性,customText是一個(gè)string類型,表示MyView要顯示的文本,customColor是color類型,表示文本的顏色。
對(duì)項(xiàng)目進(jìn)行編譯之后會(huì)生成R.java文件,R.java文件對(duì)應(yīng)著R類。如果是Android Studio項(xiàng)目,那么R文件的目錄是app\build\generated\source\r\debug\com\ispring\attr\R.java,在該文件中有內(nèi)部類public static final class attr,在R.attr中會(huì)發(fā)現(xiàn)有customText和customColor,如下圖所示:
R.attr.customText和R.attr.customColor分別是屬性customText和customColor的資源ID。
在使用MyView時(shí),我們可以在layout文件中為MyView設(shè)置customText和customColor兩個(gè)XML屬性。layout文件如下所示:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ispring.attr.MainActivity">
<com.ispring.attr.MyView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:customText="Hello World!"
app:customColor="#FF0000FF"
/>
</RelativeLayout>
運(yùn)行效果如下所示:
可以看出在界面上顯示了藍(lán)色的“Hello World!”文本,說明MyView的自定義屬性起作用了。
我們看一下MyView的具體實(shí)現(xiàn),MyView的代碼如下所示:
package com.ispring.attr;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;
public class MyView extends View {
//存儲(chǔ)要顯示的文本
private String mCustomText;
//存儲(chǔ)文本的顯示顏色
private int mCustomColor = 0xFF000000;
//畫筆
private TextPaint mTextPaint;
//字體大小
private float fontSize = getResources().getDimension(R.dimen.fontSize);
public MyView(Context context) {
super(context);
init(null, 0);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
//首先判斷attrs是否為null
if(attrs != null){
//獲取AttributeSet中所有的XML屬性的數(shù)量
int count = attrs.getAttributeCount();
//遍歷AttributeSet中的XML屬性
for(int i = 0; i < count; i++){
//獲取attr的資源ID
int attrResId = attrs.getAttributeNameResource(i);
switch (attrResId){
case R.attr.customText:
//customText屬性
mCustomText = attrs.getAttributeValue(i);
break;
case R.attr.customColor:
//customColor屬性
//如果讀取不到對(duì)應(yīng)的顏色值,那么就用黑色作為默認(rèn)顏色
mCustomColor = attrs.getAttributeIntValue(i, 0xFF000000);
break;
}
}
}
//初始化畫筆
mTextPaint = new TextPaint();
mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextSize(fontSize);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mCustomText != null && !mCustomText.equals("")){
mTextPaint.setColor(mCustomColor);
//將文本繪制顯示出來
canvas.drawText(mCustomText, 0, fontSize, mTextPaint);
}
}
}
我們?cè)贛yView中定義了兩個(gè)成員變量mCustomText和mCustomColor。MyView的幾個(gè)構(gòu)造函數(shù)都會(huì)調(diào)用init方法,我們重點(diǎn)看一下init方法。
傳遞給init方法的是一個(gè)AttributeSet對(duì)象,可以把它看成一個(gè)索引數(shù)組,這個(gè)數(shù)組里面存儲(chǔ)著屬性的索引,通過索引可以得到XML屬性名和屬性值。
-
通過調(diào)用AttributeSet的getAttributeCount()方法可以獲得XML屬性的數(shù)量,然后我們就可以在for循環(huán)中通過索引遍歷AttributeSet的屬性名和屬性值。AttributeSet中有很多getXXX方法,一般必須的參數(shù)都是索引號(hào),說幾個(gè)常用的方法:
通過AttributeSet的
public abstract String getAttributeName (int index)方法可以得到對(duì)應(yīng)索引的XML屬性名。通過AttributeSet的
public abstract int getAttributeNameResource (int index)方法可以得到對(duì)應(yīng)索引的XML屬性在R.attr中的資源ID,例如R.attr.customText、R.attr.customColor。如果index對(duì)應(yīng)的XML屬性的format是string,那么通過AttributeSet的
public abstract String getAttributeName (int index)方法,可以得到對(duì)應(yīng)索引的XML屬性的值,該方法返回的是String。除此之外,AttributeSet還有g(shù)etAttributeIntValue、getAttributeFloatValue、getAttributeListValue等方法,返回不同類型的屬性值。
-
我們通過attrs.getAttributeNameResource(i)得到獲取attr的資源ID,然后對(duì)attrResId進(jìn)行switch判斷:
如果是R.attr.customText,表示當(dāng)前屬性是customText,我們通過attrs.getAttributeValue(i)讀取customText屬性值,并將其賦值給成員變量mCustomText。
如果是R.attr.customColor,表示當(dāng)前屬性是customColor,由于Android中用一個(gè)4字節(jié)的int型整數(shù)表示顏色,所以我們通過attrs.getAttributeIntValue(i, 0xFF000000)讀取了customColor的顏色值,并將其賦值給成員變量mCustomColor。
我們重寫了MyView的onDraw方法,通過執(zhí)行mTextPaint.setColor(mCustomColor)把畫筆設(shè)置成mCustomColor的顏色,通過執(zhí)行canvas.drawText(mCustomText, 0, fontSize, mTextPaint)將mCustomText繪制到界面上。這樣,MyView就使用了customText和customColor這兩個(gè)XML屬性。
我們上面定義的customText和customColor這兩個(gè)<attr>屬性都是直接在<resources>節(jié)點(diǎn)下定義的,這樣定義<attr>屬性存在一個(gè)問題:不能想通過style或theme設(shè)置這兩個(gè)屬性的值。
要想能夠通過style或theme設(shè)置XML屬性的值,需要在<resources>節(jié)點(diǎn)下添加<declare-styleable>節(jié)點(diǎn),并在<declare-styleable>節(jié)點(diǎn)下定義<attr>,如下所示:
<resources>
<declare-styleable name="MyView">
<attr name="customText" format="string" />
<attr name="customColor" format="color" />
</declare-styleable>
</resources>
需要給<declare-styleable>設(shè)置name屬性,一般name設(shè)置為自定義View的名字,我們此處設(shè)置為MyView。
在<declare-styleable>下面定義的<attr>屬性與直接在<resources>定義的<attr>屬性其實(shí)本質(zhì)上沒有太大區(qū)別,無(wú)論哪種方式定義<attr>,都會(huì)在R.attr類中定義R.attr.customText和R.attr.customColor。不同的是,<declare-styleable name="MyView">節(jié)點(diǎn)會(huì)在R.styleable這個(gè)內(nèi)部類中有如下定義:
R.styleable.MyView是一個(gè)int數(shù)組,其值為0x7f010038和 0x7f010039。0x7f010038就是屬性R.attr.customText,0x7f010039就是屬性R.attr.customColor。也就是R.styleable.MyView等價(jià)于數(shù)組[R.attr.customText, R.attr.customColor]。R.styleable.MyView的作用會(huì)在下面介紹。
我們同樣可以在R.styleable中發(fā)現(xiàn)R.styleable.MyView_customColor和R.styleable.MyView_customText這兩個(gè)ID。<declare-styleable name="MyView">中的name加上里面的<attr>屬性的name就組成了R.styleable中的MyView_customColor和MyView_customText,中間以下劃線連接。如下圖所示:
其中R.styleable.MyView_customColor對(duì)應(yīng)R.attr.customColor,R.styleable.MyView_customText對(duì)應(yīng)R.attr.customText。MyView_customColor和MyView_customText的作用在下面介紹。
在<declare-styleable>中定義的<attr>在MyView中需要通過調(diào)用theme的obtainStyledAttributes方法來讀取解析屬性值。obtainStyledAttributes有三個(gè)重載方法,簽名分別如下所示:
public TypedArray obtainStyledAttributes (int[] attrs)
public TypedArray obtainStyledAttributes (int resid, int[] attrs)
public TypedArray obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
我們先看第三個(gè)最復(fù)雜的方法,在MyView中使用方法如下所示:
private void init(AttributeSet attributeSet, int defStyle) {
//首先判斷attributeSet是否為null
if(attributeSet != null){
//獲取當(dāng)前MyView所在的Activity的theme
Resources.Theme theme = getContext().getTheme();
//通過theme的obtainStyledAttributes方法獲取TypedArray對(duì)象
TypedArray typedArray = theme.obtainStyledAttributes(attributeSet, R.styleable.MyView, 0, 0);
//獲取typedArray的長(zhǎng)度
int count = typedArray.getIndexCount();
//通過for循環(huán)遍歷typedArray
for(int i = 0; i < count; i++){
//通過typedArray的getIndex方法獲取指向R.styleable中對(duì)應(yīng)的屬性ID
int styledAttr = typedArray.getIndex(i);
switch (styledAttr){
case R.styleable.MyView_customText:
//如果是R.styleable.MyView_customText,表示屬性是customText
//通過typedArray的getString方法獲取字符串值
mCustomText = typedArray.getString(i);
break;
case R.styleable.MyView_customColor:
//如果是R.styleable.MyView_customColor,表示屬性是customColor
//通過typedArray的getColor方法獲取整數(shù)類型的顏色值
mCustomColor = typedArray.getColor(i, 0xFF000000);
break;
}
}
//在使用完typedArray之后,要調(diào)用recycle方法回收資源
typedArray.recycle();
}
...
}
我們?cè)趓es/valeus/styles.xml文件中定義了如下style:
<style name="RedStyle">
<item name="customText">customText in RedStyle</item>
<!-- 紅色 -->
<item name="customColor">#FFFF0000</item>
</style>
然后我們?cè)趌ayout文件中將MyView的style屬性設(shè)置為上面的style,如下所示:
<com.ispring.attr.MyView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:customText="customText in AttributeSet"
style="@style/RedStyle"
/>
運(yùn)行效果如下所示:
我們雖然沒有直接設(shè)置MyView的customText和customColor兩個(gè)屬性,但是通過設(shè)置style屬性之后,在效果上RedStyle中所定義的屬性值應(yīng)用到了MyView上了。
下面我們就來解釋一下init方法:
首先我們通過
getContext().getTheme()獲取到了當(dāng)前MyView所在的Activity的theme。然后調(diào)用方法
theme.obtainStyledAttributes(attributeSet, R.styleable.MyView, 0, 0),該方法返回一個(gè)TypedArray對(duì)象。TypedArray是一個(gè)數(shù)組,通過該數(shù)組可以獲取應(yīng)用了style和theme的XML屬性值。上面這個(gè)方法有四個(gè)參數(shù),后面兩個(gè)參數(shù)都是0,大家暫且忽略不計(jì),后面會(huì)介紹。第一個(gè)參數(shù)還是AttributeSet對(duì)象,第二個(gè)參數(shù)是一個(gè)int類型的數(shù)組,該數(shù)組表示想要獲取的屬性值的屬性的R.attr中的ID,此處我們傳入的是R.styleable.MyView,在上面我們已經(jīng)提到其值等價(jià)于[R.attr.customText, R.attr.customColor],表示我們此處想獲取customText和customColor這兩個(gè)屬性的值。如果在layout文件中直接為MyView設(shè)置了某些XML屬性,那么這些XML屬性及其屬性值就會(huì)出現(xiàn)在AttributeSet中,那么Android就會(huì)直接使用AttributeSet中該XML屬性值作為
theme.obtainStyledAttributes()的返回值,比如在上面的例子中,我們通過app:customText="customText in AttributeSet"設(shè)置了MyView的XML屬性,最終運(yùn)行的效果顯示的也是文本”customText in AttributeSet”。如果在layout文件中沒有為MyView設(shè)置某個(gè)XML屬性,但是給MyView設(shè)置了style屬性,例如
style="@style/RedStyle",并且在style中指定了相應(yīng)的XML屬性,那么Android就會(huì)用style屬性所對(duì)應(yīng)的style資源中的XML屬性值作為theme.obtainStyledAttributes()的返回值。比如在上面的例子中,我們?cè)趌ayout文件中沒有設(shè)置app:customColor的值,但是在其style屬性所對(duì)應(yīng)的RedStyle資源中將customColor設(shè)置成了紅色#FFFF0000,最終文本也是以紅色顯示在界面上的。
通過以上描述,我們可以知道,View的style屬性對(duì)應(yīng)的style資源中定義的XML屬性值其實(shí)是View直接在layou文件中定義XML屬性值的替補(bǔ)值,是用于補(bǔ)漏的,AttributeSet(即在layout中直接定義XML屬性)的優(yōu)先級(jí)高于style屬性中資源所定義的屬性值。
我們?cè)倏匆幌路椒?code>obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)中的第三個(gè)參數(shù)defStyleAttr,這個(gè)參數(shù)表示的是一個(gè)<style>中某個(gè)屬性的ID,當(dāng)Android在AttributeSet和style屬性所定義的style資源中都沒有找到XML屬性值時(shí),就會(huì)嘗試查找當(dāng)前theme(theme其實(shí)就是一個(gè)<style>資源)中屬性為defStyleAttr的值,如果其值是一個(gè)style資源,那么Android就會(huì)去該資源中再去查找XML屬性值??赡苈犉饋肀容^費(fèi)勁,我們舉個(gè)例子。
我們更改了values/styles.xml文件,如下所示:
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="myViewStyle">@style/GreenStyle</item>
</style>
<style name="GreenStyle">
<item name="customText">customText in GreenStyle</item>
<item name="customColor">#FF00FF00</item>
</style>
<style name="HelloWorldStyle">
<item name="customText">Hello World!</item>
</style>
<style name="RedStyle">
<item name="customText">customText in RedStyle</item>
<item name="customColor">#FFFF0000</item>
</style>
</resources>
我們添加了HelloWorldStyle和GreenStyle,其中HelloWorldStyle只定義了customText的屬性值,而GreenStyle同時(shí)定義了customText和customColor的值,其中customColor定義為綠色。
在AppTheme中,我們?cè)O(shè)置了myViewStyle這個(gè)屬性的值,如下所示:
<item name="myViewStyle">@style/GreenStyle</item>
myViewStyle這個(gè)屬性是在values/attrs_my_view.xml中定義的,如下所示:
<resources>
<attr name="myViewStyle" format="reference" />
<declare-styleable name="MyView">
<attr name="customText" format="string" />
<attr name="customColor" format="color" />
</declare-styleable>
</resources>
myViewStyle被定義為一個(gè)reference格式,即其值指向一個(gè)資源類型,我們?cè)贏ppTheme中將其賦值為@style/GreenStyle,即在AppTheme中,myViewStyle的就是GreenStyle,其指向了一個(gè)style資源。
我們更改MyView代碼,如下所示:
//通過theme的obtainStyledAttributes方法獲取TypedArray對(duì)象
TypedArray typedArray = theme.obtainStyledAttributes(attributeSet, R.styleable.MyView, R.attr.myViewStyle, 0);
注意,上面obtainStyledAttributes方法最后一個(gè)參數(shù)還是為0,可以忽略,但是第三個(gè)參數(shù)的值不再是0,而是R.attr.myViewStyle。
然后我們更新layout文件,如下所示:
<com.ispring.attr.MyView
android:layout_width="match_parent"
android:layout_height="match_parent"
style="@style/HelloWorldStyle"
/>
我們?yōu)镸yView設(shè)置了style屬性,其值為HelloWorldStyle。
程序運(yùn)行效果如下所示:
我們發(fā)現(xiàn)結(jié)果是綠色的“Hello World!”,我們解釋一下原因。
由于這次我們沒有通過layout文件直接設(shè)置MyView的XML屬性的值,所以AttributeSet本身是沒有XML屬性值的,我們直接忽略掉AttributeSet。
我們通過
style="@style/HelloWorldStyle"為MyView設(shè)置了style為HelloWorldStyle,HelloWorldStyle中定義customText的屬性值為”Hello World!”,所以最終customText的值就是”Hello World!”,在界面上顯示的也是該值。HelloWorldStyle中并沒有定義customColor屬性值。我們將theme.obtainStyledAttributes()方法的第三個(gè)參數(shù)設(shè)置為R.attr.myViewStyle,此處的theme就是我們上面提到的AppTheme,Android會(huì)去AppTheme中查找屬性為myViewStyle的值,我們之前提到了,它的值就是
@style/GreenStyle,即GreenStyle,由于該值是個(gè)style資源,Android就會(huì)去該資源中查找customColor的值,GreenStyle定義了customColor的顏色為綠色,所以MyView最終所使用的customColor的值就是綠色。
綜上,我們發(fā)現(xiàn),此處的第三個(gè)參數(shù)的作用是:當(dāng)在AttributeSet和style屬性中都沒有找到屬性值時(shí),就去Theme的某個(gè)屬性(即第三個(gè)參數(shù))中查看其值是否是style資源,如果是style資源再去這個(gè)style資源中查找XML屬性值作為替補(bǔ)值。
<a name="t3" style="box-sizing: border-box; background: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: normal; outline: none;"></a>obtainStyledAttributes方法之defStyleRes
最后我們看一下方法obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)中的第四個(gè)參數(shù)defStyleRes。與defStyleAttr類似,defStyleRes是前面幾項(xiàng)的替補(bǔ)值,defStyleRes的優(yōu)先級(jí)最低。與defStyleAttr不同的是,defStyleRes本身直接表示一個(gè)style資源,而theme要通過屬性defStyleAttr間接找到style資源。
我們添加了BlueStyle這個(gè)style,如下所示:
<style name="BlueStyle">
<item name="customText">customText in BlueStyle</item>
<item name="customColor">#FF0000FF</item>
</style>
并將layout文件改為如下所示:
<com.ispring.attr.MyView
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
我們更改MyView代碼如下所示:
TypedArray typedArray = theme.obtainStyledAttributes(attributeSet, R.styleable.MyView, 0, R.style.BlueStyle);
第三個(gè)參數(shù)設(shè)置為0,第四個(gè)參數(shù)不再是0,而是R.style.BlueStyle。運(yùn)行界面如下所示:
只有第三個(gè)參數(shù)defStyleAttr為0或者該屬性在theme中找不到時(shí),才會(huì)使用第四個(gè)參數(shù)defStyleRes。如果第三個(gè)參數(shù)defStyleAttr不為0,但是theme的defStyleAttr所對(duì)應(yīng)的屬性值中的style沒有定義任何XML屬性值,那么第四個(gè)參數(shù)也不會(huì)defStyleRes被使用。
<a name="t4" style="box-sizing: border-box; background: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: normal; outline: none;"></a>總結(jié)
可以不通過
<declare-styleable>節(jié)點(diǎn)定義XML屬性,不過還是建議將XML屬性定義在<declare-styleable>節(jié)點(diǎn)下,因?yàn)檫@樣Android會(huì)在R.styleable下面幫我們生成很多有用的常量供我們直接使用。obtainStyledAttributes方法中,優(yōu)先級(jí)從高到低依次是:直接在layout中設(shè)置View的XML屬性值(AttributeSet) > 設(shè)置View的style屬性 > defStyleAttr > defStyleRes