https://blog.csdn.net/zhao123h/article/details/52210732

在开发android开发过程中,很多人都会遇到自定义view,一般都需要继承自View类,而当你打开View类的源码时,发现会有四个构造函数,那么这四个构造函数是如何使用的呢,怎么合理的利用四个构造函数呢,本文将进行一定探究,希望能够抛砖引玉。

一 View类的四个构造函数
先从android源码中把四个构造函数拉出来看看。。

1 第一个构造函数
/**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
*/
public View(Context context)
2 第二个构造函数
/**
* Constructor that is called when inflating a view from XML. This is called
* when a view is being constructed from an XML file, supplying attributes
* that were specified in the XML file. This version uses a default style of
* 0, so the only attribute values applied are those in the Context's Theme
* and the given AttributeSet.
*
* <p>
* The method onFinishInflate() will be called after all children have been
* added.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @see #View(Context, AttributeSet, int)
*/
public View(Context context, @Nullable AttributeSet attrs)
3 第三个构造函数
/**
* Perform inflation from XML and apply a class-specific base style from a
* theme attribute. This constructor of View allows subclasses to use their
* own base style when they are inflating. For example, a Button class's
* constructor would call this version of the super class constructor and
* supply <code>R.attr.buttonStyle</code> for <var>defStyleAttr</var>; this
* allows the theme's button style to modify all of the base view attributes
* (in particular its background) as well as the Button class's attributes.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @param defStyleAttr An attribute in the current theme that contains a
* reference to a style resource that supplies default values for
* the view. Can be 0 to not look for defaults.
* @see #View(Context, AttributeSet)
*/
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr)
4 第4个构造函数
/**
* Perform inflation from XML and apply a class-specific base style from a
* theme attribute or style resource. This constructor of View allows
* subclasses to use their own base style when they are inflating.
* <p>
* When determining the final value of a particular attribute, there are
* four inputs that come into play:
* <ol>
* <li>Any attribute values in the given AttributeSet.
* <li>The style resource specified in the AttributeSet (named "style").
* <li>The default style specified by <var>defStyleAttr</var>.
* <li>The default style specified by <var>defStyleRes</var>.
* <li>The base values in this theme.
* </ol>
* <p>
* Each of these inputs is considered in-order, with the first listed taking
* precedence over the following ones. In other words, if in the
* AttributeSet you have supplied <code><Button * textColor="#ff000000"></code>
* , then the button's text will <em>always</em> be black, regardless of
* what is specified in any of the styles.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @param defStyleAttr An attribute in the current theme that contains a
* reference to a style resource that supplies default values for
* the view. Can be 0 to not look for defaults.
* @param defStyleRes A resource identifier of a style resource that
* supplies default values for the view, used only if
* defStyleAttr is 0 or can not be found in the theme. Can be 0
* to not look for defaults.
* @see #View(Context, AttributeSet, int)
*/
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)

这四个构造函数在源码中都有注释,为了便于理解,我将注释也贴了出来,有点长,不过利于本文理解。

二 构造函数的调用
【第一个问题】

如果我们在继承了View类实现自定义类时,需要重写哪个构造函数?

回答这个问题,首先需要知道,在定义了View时,我们都调用了哪个构造函数。我这里做了一个简单的实验:

我们声明一个简单的View,继承自TextView,什么都不改,只是在4个constructor中打印几个tag,查看到底哪个构造函数被调用。

【实验】

package com.zhaohui.code.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;

import com.example.zhaohui.player.R;

/**
* Created by zhaohui on 16-8-12.
*/
public class CustomView extends TextView {
String tag = "customView";

public CustomView(Context context) {
super(context);
Log.d(tag,"First Constructor");
}

public CustomView(Context context, AttributeSet attrs) {
this(context,attrs,android.R.attr.textViewStyle);
Log.d(tag,"Second Constructor");

for(int i=0;i<attrs.getAttributeCount();i++){
Log.d(tag, attrs.getAttributeName(i)+" : "+attrs.getAttributeValue(i));
}

}

public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr,0);
Log.d(tag,"Third Constructor");
}

public CustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr,defStyleRes);
Log.d(tag,"Fourth Constructor");
}

}

在布局文件layout中加上这个View

<com.zhaohui.code.view.CustomView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="CustomView"
android:textSize="30sp"/>

[实验结果]

08-14 05:08:37.847 13687-13687/com.example.zhaohui.player D/customView: Second Constructor

08-14 05:08:37.847 13687-13687/com.example.zhaohui.player D/customView: textSize   :   30.0sp

08-14 05:08:37.847 13687-13687/com.example.zhaohui.player D/customView: layout_width   :   -1

08-14 05:08:37.848 13687-13687/com.example.zhaohui.player D/customView: layout_height   :   -2

08-14 05:08:37.848 13687-13687/com.example.zhaohui.player D/customView: text   :   CustomView

【实验—结果分析】

通过上面的实验输出中的Second Constructor,我们知道,当我们自定义一个View,且在布局文件中引用时,在系统初始化该View时,调用的是第二个构造函数,而且我还把第二个构造函数中的attrs参数也打了出来,可以看出其中的参数attrs是我们在xml中配置的参数。

其实第一个构造函数用途并不大,主要是在java代码中声明一个View时所用,不过如果只用第一个构造函数,声明的View并没有任何的参数,基本是个空的View对象。

三 View的第三和第四个构造函数
在回答了第一个问题后,还有后两个构造函数,这是本文的重点。

1 View的属性和主题
     在说后两个构造函数之前,先说说View的属性,在View中有不同的属性,比如layout_width等,TextView还有textColor这些特有的属性,我们可以对这些属性进行不同的配置进而实现不同的效果。而且属性也可以在不同的位置进行配置。以TextView为例,android:textColor这个属性可以在多个地方配置,可以直接写在xml中,可以在xml中以style的形式定义,这两种是我们平时见得较多的,其实还有一种背后的力量可以给属性赋值,那就是主题。

我们在android中可以配置一个主题,从而使得一些View即使你不对其进行任何配置,它都会有一些已经默认赋值的属性,这就是主题的功劳。

View类的后两个构造函数都是与主题相关的,也就是说,在你自定义View时,如果不需要你的View随着主题变化而变化,有前两个构造函数就OK了,但是如果你想你的View随着主题变化而变化,就需要利用后两个构造函数了。

2 属性赋值的优先级
         当可以在多个地方赋值属性时,一个问题就不可避免的出现了:优先级!!!

一个属性可以在多个地方赋值,xml定义,xml中引入style,theme中直接指定,defStyleAttr,defStyleRes 这5个地方。(后面会将这几个地方的用处)

【第二个问题】

属性在多个地方被赋值后,系统以哪个属性为准呢?

我将用一个实验,利用多个TextView整体说明属性赋值的优先级,这个实验将贯穿文章后面,我将分块讲解。

【实验】

首先我们定义一个style文件,从style中可以看出,我们定义了一个主题,主题中有两种定义textView颜色的形式,一种是对textViewStyle进行定义(蓝色),一种是直接对textColor进行定义(紫色)。这是主题运用的两种方式,后面详述,现在我们只需要知道,我们的主题默认不是白色的!!!!

后面的几种style,每种对应一个颜色,用来区分优先级。

【实验 - style文件】

<resources>

<!-- Base application theme. -->
<style name="AppTheme" parent="@android:style/Theme.Holo">
<item name="android:textViewStyle">@style/BlueTextStyle</item>
<item name="android:textColor">@android:color/holo_purple</item>
</style>

<style name ="BlueTextStyle">
<item name="android:textColor">@android:color/holo_blue_light</item>
</style>

<style name ="RedTextStyle">
<item name="android:textColor">@android:color/holo_red_light</item>
</style>

<style name ="OrangeTextStyle">
<item name="android:textColor">@android:color/holo_orange_light</item>
</style>

<style name ="GreenTextStyle">
<item name="android:textColor">@android:color/holo_green_light</item>
</style>
</resources>

【实验 - 布局文件】

我们声明一个layout文件,里面有多个TextView和我自定义的View,现在我们自需要现在只看前三个TextView

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

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Normal TextView"
android:textSize="30sp"/>
<TextView
style="@style/RedTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Red style TextView"
android:textSize="30sp"/>

<TextView
style="@style/RedTextStyle"
android:textColor="@android:color/holo_orange_light"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="XML defined orangeTextView"
android:textSize="30sp"/>

<com.zhaohui.code.view.CustomView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="CustomView"
android:textSize="30sp"/>
<com.zhaohui.code.view.CustomBlankView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="CustomBlankView !"
android:textSize="30sp"/>
<com.zhaohui.code.view.CustomGreenView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="CustomGreenView !"
android:textSize="30sp"/>
<com.zhaohui.code.view.CustomGreenView
android:textColor="@android:color/holo_orange_light"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Custom styled Red TextView !"
android:textSize="30sp"/>
<com.zhaohui.code.view.CustomGreenView
style="@style/RedTextStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Custom styled Red TextView !"
android:textSize="30sp"/>
</LinearLayout>
【实验结果】

【实验 - 结果分析1】

我们现在只看前三个TextView

第一个由于主题的原因,是蓝色

第二个 style="@style/RedTextStyle",颜色是红色,说明优先级style>theme

第三个style和xml定义同时存在,

style="@style/RedTextStyle

android:textColor="@android:color/holo_orange_light"

显示 橙色,说明优先级xml定义>style>theme

因此我们得到结论1:

【结论1】:优先级xml定义>style>theme

【实验 - 结果分析2】

但是theme的分析远没有结束,我们刚才在定义主题时,有两个赋值,

<style name="AppTheme" parent="@android:style/Theme.Holo">
<item name="android:textViewStyle">@style/BlueTextStyle</item>
<item name="android:textColor">@android:color/holo_purple</item>
</style>

为什么TextView默认的颜色是蓝色,而非紫色呢?????

这就需要研究View是如何利用系统主题,这时候需要回到我们今天的主题:View的构造函数!!!!

View中如何体现主题的信息,需要就通过实验对View进行彻底探究。这是一个复杂的问题,需要看看View的第三和第四个构造函数,在看这两个构造函数时,就不可避免的看到两个让人懵b的参数:defStyleAttr和defStyleRes。这时引入我们的第三个问题。

【第三个问题】

那么在View的第四个构造函数中的后面两个的参数都是什么意思呢?

我们首先看看View的注释:

* @param defStyleAttr An attribute in the current theme that contains a
* reference to a style resource that supplies default values for
* the view. Can be 0 to not look for defaults.</span><span style="background:rgb(255,255,255)">
第四个构造函数中第三个参数defStyleAttr,从名字就能看出,是一个属性资源。

这个属性资源跟主题有一个奇妙的协议:只要在主题中对这个属性赋值,该View就会自动应用这个属性的值。

再看看在第四个构造函数中有一个参数defStyleRes,这个参数是什么作用呢?

先看注释:

@param defStyleRes A resource identifier of a style resource that
* supplies default values for the view, used only if
* defStyleAttr is 0 or can not be found in the theme. Can be 0
* to not look for defaults.</span>

这个参数说,只有在第三个参数defStyleAttr为0,或者主题中没有找到这个defStyleAttr属性的赋值时,才可以启用。而且这个参数不再是Attr了,而是真正的style。其实这也是一种低级别的“默认主题”,即在主题未声明属性值时,我们可以主动的给一个style,使用这个构造函数定义出的View,其主题就是这个定义的defStyleRes(是一种写死的style,因此优先级被调低)。

【源码实例】

我们看一下在TextView中是如何给defStyleAttr和defStyleRes这两个参数赋值的。查看TextView的源码中,四个构造函数:

public TextView(Context context) {
this(context, null);
}

public TextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.textViewStyle);
}

public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}

@SuppressWarnings("deprecation")
public TextView(
Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
......
}

可以看出,TextView的第2个构造函数直接调用了第3个构造函数,只是传了一个com.android.internal.R.attr.textViewStyle的参数,第三个在调用第四个构造函数时,最后一个参数是0.

【TextView 的defStyleAttr和defStyleRes】

也就是说在TextView中,TextView的第二个构造函数传入的defStyleAttr是com.android.internal.R.attr.textViewStyle。

那么这个com.android.internal.R.attr.textViewStyle是个什么鬼呢,我们从源码中看看。

查看/Sdk/platforms/android-23/data/res/values这个路径中找个一个attrs.xml文件,打开看看,找到textViewStyle,如下所示,哦,原来是一个reference类型的属性,因此在给这个属性赋值时,在xml中一般使用@style/xxx形式就可以了。

<declare-styleable name="Theme">
......
<attr name="textViewStyle" format="reference"/>
......
</declare-styleable>
app的主题可以为这个textViewStyle属性提供一套默认的style资源。比如在本例中,我们的主题继承自Theme.Holo中,在Theme.Holo中有一个item如下:

<item name="textViewStyle">@style/Widget.Holo.TextView</item>
说明在Theme.Holo中,textViewStyle指向@style/Widget.Holo.TextView
因此默认情况下,TextView的属性都是在Widget.Holo.TextView这个Style中(但是其实这个style中并没有对textColor进行定义,有兴趣的可以自己去看看)。

在本例中我们自己定义了主题,通过继承Theme.Holo主题,修改这个textViewStyle的reference,使得textViewStyle指向了蓝色主题,如下所示。因此本文中app的TextView默认颜色是蓝色。

<style name="AppTheme" parent="@android:style/Theme.Holo">
<item name="android:textViewStyle">@style/BlueTextStyle</item>
<item name="android:textColor">@android:color/holo_purple</item>
</style>

但是,我们的主题的内容并没有完结,很明显,我们在主题中还有一个android:textColor的赋值。

在同时使用了defStyleAttr(即主题中定义的textViewStyle)和主题直接定义时,显示了defStyleAttr的定义,说明使用了defStyleAttr的优先级要比直接在主题中声明优先级高。因此我们又得到一个结论。

【结论2】:优先级 defStyleAttr>theme直接定义

【第四个问题】

从上文中我们知道了defStyleAttr和theme的顺序,那么defStyleRes的优先级呢?

现在需要确定defStyleRes的优先级了,我们重新回到我们的实验,我们的实验里,构造了三种自定义的View, CustomView, CustomBlankView和CustomGreenView。

CustomView的代码上面已写,现在将剩下两种自定义的View代码展示如下:

【实验 - CustemGreenView类】

package com.zhaohui.code.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;

import com.example.zhaohui.player.R;

/**
* Created by zhaohui on 16-8-12.
*/
public class CustomGreenView extends TextView {
String tag = "customGreen";

public CustomGreenView(Context context) {
super(context);
}

public CustomGreenView(Context context, AttributeSet attrs) {
this(context,attrs,0);

}

public CustomGreenView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr,R.style.GreenTextStyle);
}

public CustomGreenView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr,defStyleRes);
}

}

【实验 - CustomBlankView 类】

package com.zhaohui.code.view;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.TextView;

/**
* Created by zhaohui on 16-8-12.
*/
public class CustomBlankView extends TextView {
String tag = "customGreen";

public CustomBlankView(Context context) {
super(context);
}

public CustomBlankView(Context context, AttributeSet attrs) {
this(context,attrs,0);

}

public CustomBlankView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr,0);
}

public CustomBlankView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr,defStyleRes);
}

}

【实验-结果分析3】

在CustomGreenView中,defStyleAttr被赋值为0,defStyleRes赋值为R.style.GreenTextStyle,即我们的绿色style。

在CustomBlankView中,defStyleAttr和defStyleRes都为0,此时的颜色是紫色,即直接在theme中声明的颜色。

说明在同时在defStyleRes和主题中声明时,优先显示defStyleRes.由此又得出一个结论。

【结论3】:优先级 defStyleRes>theme直接定义

在实验的最后两个还是说明了直接在xml中写属性的优先级较高,即

【结论4】:优先级 xml直接定义>xml的style定义>theme直接定义

【小结】

在Theme中的优先级主要涉及到三个部分:defStyleAttr,defStyleRes和主题直接定义

我们需要分三种情况,在构造函数中,

1 当defStyleAttr!=0时,

主题中如果对defStyleAttr属性进行赋值,显示对defStyleAttr的赋值,优先级最高!

2 当(defStyleAttr==0或主题没有对defStyleAttr进行赋值)&& defStyleRes!=0而且theme中没有定义时时,显示defStyleRes,优先级中

3 如果defStyleAttr==0且defStyleRes==0时,显示theme直接定义,优先级最低

由此我们得到属性赋值总体优先级:

【结论 总】属性赋值优先级   Xml定义 > xml的style定义 > defStyleAttr > defStyleRes> theme直接定义

四 总结

在View类中有四个构造函数,涉及到多个参数,

Context:上线文,这个不用多说

AttributeSet attrs: 从xml中定义的参数

int defStyleAttr :主题中优先级最高的属性

int defStyleRes  : 优先级次之的内置于View的style

在android中的属性可以在多个地方进行赋值,涉及到的优先级排序为:

Xml直接定义 > xml中style引用 > defStyleAttr > defStyleRes > theme直接定义

总体来说,本文是对android中View的四个构造函数的探究,主要涉及到View属性的优先级问题,希望大家发现问题或不足时及时跟我联系。
————————————————
版权声明:本文为CSDN博主「学渣的第六感」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhao123h/article/details/52210732

Android 自定义View 四个构造函数详解的更多相关文章

  1. Android 自定义View修炼-Android开发之自定义View开发及实例详解

    在开发Android应用的过程中,难免需要自定义View,其实自定义View不难,只要了解原理,实现起来就没有那么难. 其主要原理就是继承View,重写构造方法.onDraw,(onMeasure)等 ...

  2. 【转载】自定义View学习笔记之详解onMeasure

    网上对自定义View总结的文章都很多,但是自己还是写一篇,好记性不如多敲字! 其实自定义View就是三大流程,onMeasure.onLayout.onDraw.看名字就知道,onMeasure是用来 ...

  3. Android 自定义View (四) 视频音量调控

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24529807 今天没事逛eoe,看见有人求助要做一个下面的效果,我看下面一哥们说 ...

  4. 在进程View时的四个构造函数详解

    public View(Context context):源代码中的解释如下:在Code中实例化一个View就会调用第一个构造函数 /** * Simple constructor to use wh ...

  5. Android 自定义 View 详解

    View 的绘制系列文章: Android View 绘制流程之 DecorView 与 ViewRootImpl Android View 的绘制流程之 Measure 过程详解 (一) Andro ...

  6. 【朝花夕拾】Android自定义View篇之(四)自定义View的三种实现方式及自定义属性使用介绍

    前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/10979161.html],谢谢! 尽管Android系统提供了不少控件,但是有很多酷炫效果仍然 ...

  7. Android 自定义 view(四)—— onMeasure 方法理解

    前言: 前面我们已经学过<Android 自定义 view(三)-- onDraw 方法理解>,那么接下我们还需要继续去理解自定义view里面的onMeasure 方法 推荐文章: htt ...

  8. Android自定义View学习(四)

    硬件加速 参考:HenCoder Android 自定义 View 1-8 硬件加速 硬件加速能够让绘制变快,主要有三个原因: 本来由 CPU 自己来做的事,分摊给了 GPU 一部分,自然可以提高效率 ...

  9. Android自定义View(RollWeekView-炫酷的星期日期选择控件)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/53420889 本文出自:[openXu的博客] 目录: 1分析 2定义控件布局 3定义Cus ...

随机推荐

  1. Redis与Python的交互

    驱动模块 redis模块用来在Python环境下驱动Redis数据库 可以直接用pip方式安装 pip install redis 或者国内镜像下载: pip install -i https://p ...

  2. 原生Form 和 Form组件 Modelform

    主要的文件:manage.py,url.py,views.py,settings.py,models.py manage.py:项目管理文件,一般不做修改. url.py:路由系统,配置views.p ...

  3. mybatis之关联关系映射

    Mybatis关联关系映射 1.一对多 首先先用逆向生成工具生成t_hibernate_order.t_hibernate_order_item 这两张表对应的model与mapper OrderVo ...

  4. HttpClient SSL connection could not be established error

    系统从.net framework 升级到dotnet core2.1 原先工作正常的httpclient,会报SSL connection could not be established erro ...

  5. 洛谷 P3375 【模板】KMP字符串匹配 题解

    KMP模板,就不解释了 #include<iostream> #include<cstdio> #include<cstring> #include<algo ...

  6. 洛谷 AT2434 JOI 公園 (JOI Park) 题解

    人生第一次AC黑题,我太感动了. 每日一题 day31 打卡 Analysis 先跑遍DJ,求出1到 i的最短路.得到每个点到 1号点的距离后,从小到大排序一遍,这时便可以枚举每个点到 1号点的距离修 ...

  7. Dominating Patterns (AC 自动鸡模版题, 出现次数最多的子串)

    传送门 题意: 给你n个模式串, 再给你一个 文本串,问模式串在文本串中出现次数最多是多少. 出现次数最多的模式串有哪些. 解: 模版题. #include <bits/stdc++.h> ...

  8. RookeyFrame 附件 上传附件

    上传附件可允许的格式: 位置:Rookey.Frame.Web\Config\upload.xml 节点:Attachment -> allowType

  9. C++ 如何进阶?

    1.C++的用途和意义 总体来说,C++作为一门软件开发语言,它的流行度是在减少的.主要原因在于语言的复杂和灵活导致软件开发成本提高,这体现在开发周期和人力上.它不适用于startup公司的快速开发, ...

  10. UOJ#469. 【ZJOI2019】开关 生成函数

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ469.html 前言 clytql当场秒掉此题可惜不知道为什么fst了. 题解 考虑构建指数生成函数. 对于第 \(i\) ...