1.引言

小菜鸟写博客,因为刚刚开始写,写的感觉很乱,如果你能看下去,表示感谢。身份小菜鸟,这篇文章看了鸿洋大神的博客,还有其他博客自己做的一个小总结。所以有什么错误请多多指正。

2.初始自定义View的构造函数

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

    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

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

    /**
     * @description 这个最小API需要21,所以基本不实现这个方法
     */
    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

根据以上构造函数先将以下几点基本的

  1. 代码里new一个View的时候,调用的是第一个构造函数
  2. 在XML布局中调用的时候,调用的是第二个构造函数
  3. 在XML布局中调用并且有自定义属性也是调用第二个构造函数

总结:说明系统只会调用前两个构造函数,第三个构造函数通常是我们在构造函数中自己调用(比如在第二个构造函数中调用第三个 this(context, attrs, 0))。具体如何调用得看你自己的需求

3.CustomView的自定义属性

1.declare-styleable 标签的作用

正常的自定义属性(我都是这样子的。。)

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="MyView">
        <attr name="titleText" format="string"/>
        <attr name="titleTextColor" format="color"/>
        <attr name="titleTextSize" format="dimension"/>

    </declare-styleable>

</resources>

有人说那我不要declare-styleable标签可以不?
答案是当然可以,但是有什么不同呢,那就请看下去

<declare-styleable name="MyView">
     <attr name="titleText" format="string" />
     <attr name="titleTextColor" format="color" />
     <attr name="titleTextSize" format="dimension" />
</declare-styleable>

调用方法:
TypedArray typedArray=context.obtainStyledAttributes(attrs,R.styleable.MyView);

自定义属性
     <attr name="titleText" format="string"/>
     <attr name="titleTextColor" format="color"/>
     <attr name="titleTextSize" format="dimension"/>

调用方法:
int[] attr=new int[]{R.attr.titleText,R.attr.titleTextColor,R.attr.titleTextSize};
TypedArray typedArray = context.obtainStyledAttributes(attrs, attr);

接着往下看
所有resources文件中声明的属性都会在R.attr类中生成对应的成员变量:
我们在attr类中找到我们定义的:

public final class R{

     public static final class attr{
         public static final int titleText=0x7f01010d;

         public static final int titleTextColor=0x7f01010e;

         public static final int titleTextSize=0x7f01010f;
    }
}

但是声明在标签中的属性,系统还会在R.styleable类中生成相关的成员变量:

public static final class styleable{
    public static final int[] MyView = {
            0x7f01010d, 0x7f01010e, 0x7f01010f
        };
    public static final int MyView_titleText = 0;
    public static final int MyView_titleTextColor = 1;
    public static final int MyView_titleTextSize = 2;
 }

可以看出R.styleable.MyView是一个数组,期中的元素就是他们的值,下面的MyView_titleText等就是它的索引

总结:根据这个例子就很直观的知道了,这个标签就是为我们提供一个属性数组。分组更加的直观。所以推荐使用该标签

2.使用系统定义的属性

根据之前定义的属性,我们在布局里是使用如下

<com.magicsoft.demoone.MyView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:titleText="我我我"
    app:titleTextColor="@color/colorPrimary"
    app:titleTextSize="16dp"
    />

看布局可知我们有一个属性叫做app:titleText="我我我"
但是系统有一个属性叫android:text

问题:这在系统中是一个语义比较明确的属性,可以直接使用吗?

答案当然是可以,如下直接在attrs中使用

<declare-styleable name="MyView">
    <attr name="android:text"/>
    <attr name="titleTextColor" format="color"/>
    <attr name="titleTextSize" format="dimension"/>
</declare-styleable>

注意,这里我们是使用已经定义好的属性,不需要去添加format属性(注意声明和使用的区别,差别就是有没有format)。
然后在类中这么获取:titleText = typedArray.getString(R.styleable.MyView_android_text);
布局文件中:android:text="我我我"

4.自定义属性详解

obtainStyledAttributes一共有四个参数分别如下

1.AttributeSet

用处:是所有属性的集合
举个例子:我们通过AttributeSet打印出所有的属性

布局文件:

<com.magicsoft.demoone.MyView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="我我我我我"
        app:titleTextSize="16dp"
        />

代码打印:

int count = attrs.getAttributeCount();
for (int i = 0; i < count; i++) {
String name = attrs.getAttributeName(i);
String value = attrs.getAttributeValue(i);
Log.e(TAG, "attrName = " + name + " , attrVal = " + value);
}

输出:

MyView: attrName = layout_width , attrVal = -1
MyView: attrName = layout_height , attrVal = -1
MyView: attrName = text , attrVal = 我我我我我
MyView: attrName = titleTextSize , attrVal = 16.0dip

很神奇对不对,所有的属性都有咧。那是不是不用TypedArray了呢。
当然不是咯。 不是都能打印吗? 咋还要用这个什么鬼TypedArray呢。
那接着往下看 我稍微修改下布局
布局:

<com.magicsoft.demoone.MyView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/me"
        app:titleTextSize="@dimen/textSize"
        />

打印代码:

int count = attrs.getAttributeCount();
      for (int i = 0; i < count; i++) {
            String name = attrs.getAttributeName(i);
            String value = attrs.getAttributeValue(i);
            Log.e(TAG, "attrName = " + name + " , attrVal = " + value);
        }                                                                                   TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView,R.attr.myView1,0);

titleText = typedArray.getString(R.styleable.MyView_android_text);
titleTextSize = typedArray.getDimension(R.styleable.MyView_titleTextSize, 16);
Log.e(TAG, "titleText = " + titleText + " , titleTextSize = " + titleTextSize);

输出

MyView: attrName = layout_width , attrVal = -1
MyView: attrName = layout_height , attrVal = -1
MyView: attrName = text , attrVal = @2131099682
MyView: attrName = titleTextSize , attrVal = @2131165282
MyView: titleText = 我我我我我 , titleTextSize = 144.0

对比发现了什么?是不是AttributeSet获取到的值打印出来都是@+一大串数字,再看typedArray打印出来的值
是不是懂了。
TypedArray其实是用来简化我们的工作的,比如上例,如果布局中的属性的值是引用类型(比如:@dimen/textSize),如果使用AttributeSet去获得最终的像素值,那么需要第一步拿到id,第二步再去解析id。而TypedArray正是帮我们简化了这个过程。

2.int[] attrs

这个参数其实前面讲过了,就是你自定义属性的集合,就是在R类中生成的int型数组

3.int defStyleAttr

当在AttributeSet和style属性中都没有找到属性值时,就去Theme的某个属性(即第三个参数)中查看其值是否是style资源,如果是style资源再去这个style资源中查找XML属性值作为替补值。
看的太绕了,那就看以下例子
先看styles.xml,如图所示

<!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        //在AppTheme中,我们设置了defStyleAttrView这个属性的值
        <item name="defStyleAttrView">@style/TextViewStyle</item>
    </style>

 //颜色设置绿色
    <style name="TextViewStyle">
        <item name="android:text">我是defStyleAttr</item> 
        <item name="titleTextColor">@color/green</item> 
        <item name="titleTextSize">@dimen/textSize</item>
    </style>

首先, 我们先声明一个refrence格式的属性, 用于表示style的引用.声明在之前的res/values/attrs_my_view.xml文件里即可:

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

接下来看布局文件和MyView代码

<com.magicsoft.demoone.MyView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="我不是defStyleAttrView,略略略略"
        app:titleTextSize="@dimen/textSize"
        />

布局中改变text内容,不设置颜色。

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView,R.attr.defStyleAttrView,0);

上面的第三个参数已经不是0而是R.attr.defStyleAttrView,但是第四个还是0。接着我们运行看效果

Android自定义view时构造函数 android 构造函数_android

我们发现字是我不是defStyleAttrView,略略略略,颜色是绿色对不对。为啥呢?

  • 文字是这个我不是defStyleAttrView,略略略略可以理解吧,因为我直接在XMl里设置了属性。
  • 颜色是绿色的,是因为XML和style属性中都没有找到颜色的属性值时,而我们在代码里填写了第三个参数R.attr.defStyleAttrView,那么Android会去AppTheme中查找属性为defStyleAttrView的值,他的值是TextViewStyle,由于该值是个style资源,Android就会去该资源中查找titleTextColor的值,TextViewStyle定义了titleTextColor的颜色为绿色,所以MyView最终所使用的titleTextColor的值就是绿色。

总结,看了例子,再看这句话当在AttributeSet和style属性中都没有找到属性值时,就去Theme的某个属性(即第三个参数)中查看其值是否是style资源,如果是style资源再去这个style资源中查找XML属性值作为替补值。 是不是就好理解一点了咧。

4. int defStyleRes

当第三个参数defStyleAttr为0或该属性在theme中找不到时,才会使用第四个参数defStyleRes.如果第三个参数defStyleAttr不为0,该属性在theme中找到,但是所对应的style没有定义任何XML的属性值,那么第四个参数也不会被使用。
defStyleRes本身直接表示一个style资源,这个就很方便
直接看例子咯
先看styles.xml和布局,给他设置个高贵紫。

<style name="defStyleResStyle">
        <item name="android:text">我是defStyleResView</item>
        <item name="titleTextColor">@color/purple</item>
        <item name="titleTextSize">@dimen/textSize</item>
    </style>

布局
    <com.magicsoft.demoone.MyView
        android:layout_width="match_parent"
        android:layout_height="match_parent"

        />

再看MyView,第三个参数是0了,第四个参数设置为R.style.defStyleResStyle

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView,0,R.style.defStyleResStyle);

看效果图

Android自定义view时构造函数 android 构造函数_构造函数_02

看到了吧,显示的是style里定义的文字和颜色吧
这里有个特别注意点:
如果第三个参数defStyleAttr不为0,该属性在theme中找到,但是所对应的style没有定义任何XML的属性值,那么第四个参数也不会被使用。