經(jīng)常在寫(xiě)自定義View的時(shí)候,我們常常會(huì)忽略一些不太起眼的東西,比如下面構(gòu)造函數(shù)
public ZTextView(Context context, AttributeSet attrs, int defStyleAttr)
我們可以看到第三個(gè)參數(shù)為<code>int defStyleAttr</code>,但是往往誰(shuí)都不會(huì)去在意,因?yàn)楹孟裨谖覀內(nèi)粘i_(kāi)發(fā)中能夠用到這個(gè)地方的并不多,今天自己也是碰到了一個(gè)問(wèn)題,陡然間想去了解下這個(gè)參數(shù)。
看下谷歌官方具體的解釋
| Parameters | 參數(shù)解釋 |
|---|---|
| defStyleAttr | int: An attribute in the current theme that contains a reference to a style resource that supplies defaults values for the TypedArray. Can be 0 to not look for defaults. |
| defStyleRes | int: A resource identifier of a style resource that supplies default values for the TypedArray, used only if defStyleAttr is 0 or can not be found in the theme. Can be 0 to not look for defaults. |
根據(jù)上述表格中解釋的內(nèi)容,我們可以說(shuō)似懂非懂吧。對(duì)于defStyleAttr我們可以簡(jiǎn)單的進(jìn)行翻譯:
在當(dāng)前包含了一個(gè)引用到為T(mén)ypedArray提供默認(rèn)值的樣式資源的theme中的一種屬性。
可以為0,但是為0的時(shí)候就不會(huì)再去尋找默認(rèn)的。(注:這里的默認(rèn)也就是defStyleRes)
ps:翻譯的有點(diǎn)渣,見(jiàn)諒!
上面的翻譯中我們大致能夠明白,凡是空的引用我們就直接給0,所以上面的0表示的是這個(gè)含義。defStyleAttr是定義在theme中的一個(gè)引用,這個(gè)引用指向一個(gè)style資源,而這個(gè)style資源包含了一些TypedArray的默認(rèn)值。
我們先從最基本的開(kāi)始,開(kāi)始定義我們的自定義布局。我目前使用的AS版本是2.1.3,至于自定義布局,老生常談的東西了,先是要新建一個(gè)attrs.xml資源文件,然后再定義一些我們自定義的布局,我的代碼如下:
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ZTextViewStyle">
<attr name="attr_1" format="string" />
<attr name="attr_2" format="string" />
<attr name="attr_3" format="string" />
<attr name="attr_4" format="string" />
</declare-styleable>
<attr name="ZTV_def_style" format="reference"/>
</resources>
很簡(jiǎn)單,然后定義我們的自定義布局,代碼如下:
ZTextView.java
package cn.zhoudl.attrdemo;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;
/**
* Created by dave on 2016/8/24 0024 17:28
*/
public class ZTextView extends TextView {
public ZTextView(Context context) {
super(context);
}
public ZTextView(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.ZTV_def_style);
}
public ZTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
parse(context, attrs, defStyleAttr);
}
private void parse(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ZTextViewStyle, defStyleAttr, 0);
String one = typedArray.getString(R.styleable.ZTextViewStyle_attr_1);
String two = typedArray.getString(R.styleable.ZTextViewStyle_attr_2);
String three = typedArray.getString(R.styleable.ZTextViewStyle_attr_3);
String four = typedArray.getString(R.styleable.ZTextViewStyle_attr_4);
log("one = " + one);
log("two = " + two);
log("three = " + three);
log("four = " + four);
typedArray.recycle();
}
private void log(String msg) {
Log.i("ZDL", msg);
}
}
也比較簡(jiǎn)單,是獲取屬性值并打印出來(lái)。
然后是我們的布局:
activity_main.xml
<?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:zt="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="cn.zhoudl.attrdemo.MainActivity">
<cn.zhoudl.attrdemo.ZTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Hello World!"
zt:attr_1="one" />
</RelativeLayout>
運(yùn)行結(jié)果如下:
08-24 20:08:52.739 27780-27780/cn.zhoudl.attrdemo I/ZDL: one = one
08-24 20:08:52.739 27780-27780/cn.zhoudl.attrdemo I/ZDL: two = null
08-24 20:08:52.739 27780-27780/cn.zhoudl.attrdemo I/ZDL: three = null
08-24 20:08:52.739 27780-27780/cn.zhoudl.attrdemo I/ZDL: four = null
可以看到只有我們xml中寫(xiě)了的屬性值被拿到了。
修改我們的布局文件:
<cn.zhoudl.attrdemo.ZTextView
style="@style/ZTV_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Hello World!"
zt:attr_1="one" />
同時(shí)新增一個(gè)ZTV_style。
<style name="ZTV_style">
<item name="attr_1">style_one</item>
<item name="attr_2">style_two</item>
</style>
再次運(yùn)行我們的程序。
08-24 20:15:34.129 9301-9301/? I/ZDL: one = one
08-24 20:15:34.129 9301-9301/? I/ZDL: two = style_two
08-24 20:15:34.129 9301-9301/? I/ZDL: three = null
08-24 20:15:34.129 9301-9301/? I/ZDL: four = null
可以發(fā)現(xiàn)我們的attr_2屬性也被拿到了。這里也說(shuō)明了一點(diǎn),我們xml中的屬性優(yōu)先級(jí)是大于style中的屬性優(yōu)先級(jí)的,這個(gè)對(duì)我們編寫(xiě)xml有時(shí)還是有點(diǎn)用處的。
好,修改我們的theme,如下:
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="attr_1">theme_one</item>
<item name="attr_2">theme_two</item>
<item name="attr_3">theme_three</item>
<item name="attr_4">theme_four</item>
<!--<item name="ZTV_def_style">@style/ZTV_theme_def_style</item>-->
</style>
再次運(yùn)行:
08-24 20:24:08.469 26327-26327/cn.zhoudl.attrdemo I/ZDL: one = one
08-24 20:24:08.469 26327-26327/cn.zhoudl.attrdemo I/ZDL: two = style_two
08-24 20:24:08.469 26327-26327/cn.zhoudl.attrdemo I/ZDL: three = theme_three
08-24 20:24:08.469 26327-26327/cn.zhoudl.attrdemo I/ZDL: four = theme_four
屬性也同樣被拿到了,所以到這里,我們可以得到一個(gè)結(jié)論:
屬性的優(yōu)先級(jí)是:xml > style > theme
其實(shí)上面說(shuō)了那么多,但是都是與defStyleAttr無(wú)關(guān)的。
是不是超級(jí)無(wú)語(yǔ)?哈哈,不過(guò)也不能說(shuō)完全無(wú)關(guān)吧,至少這些都是基礎(chǔ)。
從上面的attrs.xml的文件中我們可以看到一句:
<attr name="ZTV_def_style" format="reference"/>
下面就要與他產(chǎn)生關(guān)系了。
我們?nèi)tyles.xml添加一個(gè)style,并在AppTheme中打開(kāi)被注釋掉的item.
<style name="ZTV_theme_def_style">
<item name="attr_1">def_style_one</item>
<item name="attr_2">def_style_two</item>
<item name="attr_3">def_style_three</item>
</style>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="attr_1">theme_one</item>
<item name="attr_2">theme_two</item>
<item name="attr_3">theme_three</item>
<item name="attr_4">theme_four</item>
<item name="ZTV_def_style">@style/ZTV_theme_def_style</item>
</style>
再次運(yùn)行:
08-24 20:31:15.659 8640-8640/cn.zhoudl.attrdemo I/ZDL: one = one
08-24 20:31:15.659 8640-8640/cn.zhoudl.attrdemo I/ZDL: two = style_two
08-24 20:31:15.659 8640-8640/cn.zhoudl.attrdemo I/ZDL: three = def_style_three
08-24 20:31:15.659 8640-8640/cn.zhoudl.attrdemo I/ZDL: four = theme_four
可以看到第三個(gè)屬性的值變了,這個(gè)就是我們定義的defStyleAttr。這樣我們的結(jié)論可以進(jìn)一步延伸:
xml > style > defStyleAttr > theme
好,之前我們?nèi)傩远际怯玫倪@個(gè)
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ZTextViewStyle, defStyleAttr, 0);
我們并沒(méi)有設(shè)置defStyleRes,下面我們新增一個(gè)defStyleRes的style文件
<style name="ZTV_default_style">
<item name="attr_1">default_style_one</item>
<item name="attr_2">default_style_two</item>
<item name="attr_3">default_style_three</item>
<item name="attr_4">default_style_four</item>
</style>
更改取屬性的方法:
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ZTextViewStyle, defStyleAttr, R.style.ZTV_default_style);
再次運(yùn)行:
08-24 20:37:56.459 22084-22084/cn.zhoudl.attrdemo I/ZDL: one = one
08-24 20:37:56.459 22084-22084/cn.zhoudl.attrdemo I/ZDL: two = style_two
08-24 20:37:56.459 22084-22084/cn.zhoudl.attrdemo I/ZDL: three = def_style_three
08-24 20:37:56.459 22084-22084/cn.zhoudl.attrdemo I/ZDL: four = theme_four
結(jié)果沒(méi)有變化。
現(xiàn)在我們刪除theme中的第四個(gè)屬性:
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="attr_1">theme_one</item>
<item name="attr_2">theme_two</item>
<item name="attr_3">theme_three</item>
<item name="ZTV_def_style">@style/ZTV_theme_def_style</item>
</style>
再次運(yùn)行:
08-24 20:40:13.599 26689-26689/cn.zhoudl.attrdemo I/ZDL: one = one
08-24 20:40:13.599 26689-26689/cn.zhoudl.attrdemo I/ZDL: two = style_two
08-24 20:40:13.599 26689-26689/cn.zhoudl.attrdemo I/ZDL: three = def_style_three
08-24 20:40:13.599 26689-26689/cn.zhoudl.attrdemo I/ZDL: four = null
發(fā)現(xiàn)根本沒(méi)有去我們默認(rèn)的defStyleRes中定義的屬性,為什么呢?
請(qǐng)返回前面仔細(xì)看看Google官方的文檔,那里其實(shí)寫(xiě)了,除非defStyleAttr為0(可以理解為theme中沒(méi)有相關(guān)屬性),否則程序根本不會(huì)去從我們的defStyleRes找屬性值。所以這里你有兩種方法,第一種就是在獲取屬性的時(shí)候,在defStyleAttr屬性那里給0,第二種方法就是注釋掉theme下面的引用。
我選擇的是第二種,注釋后在運(yùn)行:
08-24 20:46:44.919 7693-7693/cn.zhoudl.attrdemo I/ZDL: one = one
08-24 20:46:44.919 7693-7693/cn.zhoudl.attrdemo I/ZDL: two = style_two
08-24 20:46:44.919 7693-7693/cn.zhoudl.attrdemo I/ZDL: three = default_style_three
08-24 20:46:44.919 7693-7693/cn.zhoudl.attrdemo I/ZDL: four = default_style_four
現(xiàn)在就成功拿到了defStyleRes中的屬性。但是我們也發(fā)現(xiàn)了這里程序不會(huì)再去theme中尋找了。
如果我們?nèi)サ鬦TV_default_style中的第三個(gè)屬性,在運(yùn)行:
08-24 20:50:10.929 14732-14732/cn.zhoudl.attrdemo I/ZDL: one = one
08-24 20:50:10.929 14732-14732/cn.zhoudl.attrdemo I/ZDL: two = style_two
08-24 20:50:10.929 14732-14732/cn.zhoudl.attrdemo I/ZDL: three = theme_three
08-24 20:50:10.929 14732-14732/cn.zhoudl.attrdemo I/ZDL: four = default_style_four
會(huì)發(fā)現(xiàn)three取了theme中的屬性。這里說(shuō)明了defStyleRes的優(yōu)先級(jí)是大于theme的。
結(jié)論:
我們解析屬性的優(yōu)先級(jí): xml > style > defStyleAttr > defStyleRes > theme
具體代碼請(qǐng)參見(jiàn):https://github.com/zhoudailiang/LearnDemos/tree/master/AttrDemo