彻底搞懂自定义控件中的四个构造方法

    在上一篇博客动手实现饼图控件写完以后,有些小伙伴说讲得不够细,建议从最基本开始讲起,比如构造函数都是什么?我觉得说得很有道理,正好自己也不够了解自定义控件中的4个构造方法的具体调用时机和它们各自的参数作用,今天终于有时间把这部分内容进行学习整理,顺便分享给那些和我一样不太了解它们的同学们!

    首先我们先将构造方法中的参数的作用做个说明,来看一下参数最多的构造方法:    

public MyCustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
}

    一共4个参数:

    (1)context:这个不用多说了,大家都知道,上下文对象.

    (2)attrs:属性集合,但是它指的是什么呢到底,抱着能动手绝不哔哔的态度,我直接将它打印出来:

实验:

先看一下布局文件的情况:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <huanju.wanka.com.MyCustomView
        xmlns:myapp="http://schemas.android.com/apk/res-auto"
        android:layout_width="300dp"
        android:layout_height="300dp"
        myapp:attr1="我是attr1,我是在xml中定义的"
        style="@style/theme_style"
        />

</RelativeLayout>

接着,我们打印一下attrs:


第3个是一个自定义属性,暂时先不用管它,主要想说明的是,这里打印出了我们在layout中对这个view设置的所有属性.这也就和它的名字对上了,"AttributeSet"——属性集合.

    (3)defStyleAttr:这个参数是指,在当前view所属Activity的Theme(或如果这个Activity没有设置Theme,那么就是指Application的Theme)对控件设置的属性

实验:

先自定义一个theme,同时给自定义属性加上一个"attr2",

<style name="theme_style" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="attr1">我是attr1,我是在theme中定义的</item>
    <item name="attr2">我是attr2,我是在theme中定义的</item>
    <item name="android:background">#00ff00</item>
</style>

然后将这个theme设置给当前的MainActivity:

<activity android:name="huanju.wanka.com.MainActivity" android:theme="@style/theme_style">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

之前打印的方式是打印attrs这个参数,但是它是代表我们在布局中对view设置的属性,而这一步我们是通过theme设置的属性,所以只能通过代码中获取属性再逐个打印,打印结果:


看结果~发现attr1的结果好像不太对劲,这里就涉及到设置属性的优先级了.由于我们在xml中对attr1属性进行了设置,那么它优先取了xml中定义的结果,而attr2在xml布局中并没有被定义,所以它取了theme中的结果.

其实讲到这里有些细心的同学会有疑问了,你这是通过theme直接将一个style直接设置给了Activity,又不是通过构造方法设置给它的.确实是这样,前面为了方便观察属性,我是方便了一下,代码中defStyleAttr接收的也是一个int类型,我们可以通过R.style.xxx设置给它.

 (4)defStyleRes:这个参数在构造方法中接收的一样是一个int类型的style,我们依然可以按照R.style.xxx的方式设置给它,那它岂不是和上面的第3个参数冲突了吗?我们还是通过动手实验来看看结果吧.

public MyCustomView(Context context, @Nullable AttributeSet attrs) {
    this (context, attrs, R.style.theme_style);
}

public MyCustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    this(context, attrs, defStyleAttr, R.style.my_view_default_resource);
}

我先在第2个构造方法中手动调用了第3个构造,并对第3个参数设置了一个style.

<style name="theme_style" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="attr1">我是theme_style的attr1</item>
    <item name="attr2">我是theme_style的attr2</item>
</style>

接下来又在第3个构造中调用了第4个构造,并对第4个参数也设置了一个style.那么此时后面2个参数都是有设置值的.

<style name="my_view_default_resource" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="attr1">我是my_view_default_resource的attr1</item>
    <item name="attr2">我是my_view_default_resource的attr2</item>
</style>

运行打印看结果:


第一个参数依然是在布局xml中定义的,之前已经说过了.可以看到attr2和attr3都是theme_style的值.根据官方对defStyleRes的说明,只有在defStyleAttr没有匹配到或置为0时,defStyleRes才会起作用.我们来试一把,将defStyleAttr设置为0跑一下.由于我上面的调用关系,直接将2个参数的构造方法中调用第3个构造方法的第3个参数改为0.

public MyCustomView(Context context, @Nullable AttributeSet attrs) {
    this (context, attrs, 0);
}

走你~运行,看结果:


打印的后面2个结果,很明显了.


    最后,总结一下,对view设置属性有4个途径,分别是:

1.直接在xml对其定义.

2.在xml中用style对view的属性进行定义.

3.通过theme对其定义.

4.如果第3条没满足或被置为0,可定义一个style设置给它.

    至此,我的理解就是一层层的对控件做些默认的属性处理,像系统的Button那样,我们平时用到Button控件时,并不需要对它设置背景,而它被展示出来总是带着一层灰色的背景,使它看上去更像一个按钮,这其实也是android系统为它做的默认属性.我们在自定义控件时,也可以通过上面那些途径对我们的自定义属性做一些默认的设置.所以,既然是默认的设置,肯定是在展示的时候默认就有的,这样我们就可以在构造方法中,通过1调用2,2调用3,3再调用4这样将我们的属性在控件的内部就设置好,而外界并不知道,也不需要知道.其实android系统的控件也是这样去做一个个通过this调用构造函数的.

    好了,本篇内容就讲到这里,内容可能有些绕,大家可以反复多看两次,再加上自己动手去试,我想大家很快就会明白这些内容.如果哪里理解得不对也希望大家指正.我们下次见.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值