深度解析View构造函数中的参数defStyleAttr

经常在写自定义View的时候,我们常常会忽略一些不太起眼的东西,比如下面构造函数

public ZTextView(Context context, AttributeSet attrs, int defStyleAttr)

我们可以看到第三个参数为<code>int defStyleAttr</code>,但是往往谁都不会去在意,因为好像在我们日常开发中能够用到这个地方的并不多,今天自己也是碰到了一个问题,陡然间想去了解下这个参数。

看下谷歌官方具体的解释

Parameters 参数解释
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.

官方链接地址

根据上述表格中解释的内容,我们可以说似懂非懂吧。对于defStyleAttr我们可以简单的进行翻译:

在当前包含了一个引用到为TypedArray提供默认值的样式资源的theme中的一种属性。
可以为0,但是为0的时候就不会再去寻找默认的。(注:这里的默认也就是defStyleRes)

ps:翻译的有点渣,见谅!

上面的翻译中我们大致能够明白,凡是空的引用我们就直接给0,所以上面的0表示的是这个含义。defStyleAttr是定义在theme中的一个引用,这个引用指向一个style资源,而这个style资源包含了一些TypedArray的默认值。

我们先从最基本的开始,开始定义我们的自定义布局。我目前使用的AS版本是2.1.3,至于自定义布局,老生常谈的东西了,先是要新建一个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>

很简单,然后定义我们的自定义布局,代码如下:

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);
    }

}

也比较简单,是获取属性值并打印出来。

然后是我们的布局:

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>

运行结果如下:

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中写了的属性值被拿到了。

修改我们的布局文件:

<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" />

同时新增一个ZTV_style。

<style name="ZTV_style">
    <item name="attr_1">style_one</item>
    <item name="attr_2">style_two</item>
</style>

再次运行我们的程序。

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

可以发现我们的attr_2属性也被拿到了。这里也说明了一点,我们xml中的属性优先级是大于style中的属性优先级的,这个对我们编写xml有时还是有点用处的。
好,修改我们的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>

再次运行:

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

属性也同样被拿到了,所以到这里,我们可以得到一个结论:

属性的优先级是:xml > style > theme

其实上面说了那么多,但是都是与defStyleAttr无关的。
是不是超级无语?哈哈,不过也不能说完全无关吧,至少这些都是基础。
从上面的attrs.xml的文件中我们可以看到一句:

<attr name="ZTV_def_style" format="reference"/>

下面就要与他产生关系了。
我们去styles.xml添加一个style,并在AppTheme中打开被注释掉的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>

再次运行:

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

可以看到第三个属性的值变了,这个就是我们定义的defStyleAttr。这样我们的结论可以进一步延伸:

xml > style > defStyleAttr > theme

好,之前我们去属性都是用的这个

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ZTextViewStyle, defStyleAttr, 0);

我们并没有设置defStyleRes,下面我们新增一个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);

再次运行:

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

结果没有变化。
现在我们删除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="ZTV_def_style">@style/ZTV_theme_def_style</item>
</style>

再次运行:

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

发现根本没有去我们默认的defStyleRes中定义的属性,为什么呢?

请返回前面仔细看看Google官方的文档,那里其实写了,除非defStyleAttr为0(可以理解为theme中没有相关属性),否则程序根本不会去从我们的defStyleRes找属性值。所以这里你有两种方法,第一种就是在获取属性的时候,在defStyleAttr属性那里给0,第二种方法就是注释掉theme下面的引用。

我选择的是第二种,注释后在运行:

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

现在就成功拿到了defStyleRes中的属性。但是我们也发现了这里程序不会再去theme中寻找了。
如果我们去掉ZTV_default_style中的第三个属性,在运行:

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

会发现three取了theme中的属性。这里说明了defStyleRes的优先级是大于theme的。

结论:

我们解析属性的优先级: xml > style > defStyleAttr > defStyleRes > theme

具体代码请参见:https://github.com/zhoudailiang/LearnDemos/tree/master/AttrDemo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容