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

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

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

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

看下谷歌官方具体的解释

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



    
        
        
        
        
    

    


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

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




    



运行结果如下:

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

修改我们的布局文件:


同时新增一个ZTV_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,如下:



再次运行:

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的文件中我们可以看到一句:


下面就要与他产生关系了。
我们去styles.xml添加一个style,并在AppTheme中打开被注释掉的item.




再次运行:

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文件


更改取属性的方法:

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中的第四个属性:



再次运行:

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

你可能感兴趣的:(深度解析View构造函数中的参数defStyleAttr)