1.自定义View的构造函数调用时机
public class MyView extends View {
public MyView(Context context) { //在代码中实例化View时调用这个构造函数
super(context);
}
public MyView(Context context, AttributeSet attrs) { //在XML中定义会调用这个构造函数
super(context, attrs);
for (int i=0;i<attrs.getAttributeCount();i++){
Log.d("MyView",attrs.getAttributeName(i));
}
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) { //第二个构造函数中可以显示调用这个构造函数
super(context, attrs, defStyleAttr);
}
}
2.自定义View属性
在Android中要增加自定义属性,需要依靠attrs.xml文件。这里指定的自定义属性,是在layout布局文件中使用的不是以android开头的属性。
res/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyView">
<attr name="textColor" format="color"/>
<attr name="textSize" format="dimension" />
<attr name="textValue" format="string" />
</declare-styleable>
</resources>
声明在declare-styleable中,系统还会为我们在R.styleable中生成相关的属性。
public static final class styleable {
public static final int[] MyView = {
0x7f0100b1, 0x7f0100b2, 0x7f0100b3
};
/*下面为上面数组的索引*/
public static final int MyView_textColor = 0;
public static final int MyView_textSize = 1;
public static final int MyView_textValue = 2;
}
在layout中声明名称空间就可以使用这些属性了(xmlns:ad=”http://schemas.android.com/apk/res-auto”)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.jimli.customviewtest.MyView
xmlns:ad="http://schemas.android.com/apk/res-auto"
android:id="@+id/MyView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
ad:textSize="20sp"/>
</LinearLayout>
那么这些属性是如何应用到自定义的View中的呢?
当我们在xml中给自定义属性赋值时 (ad:textSize=”20sp”) 当实例化属性时,这些属性会传入attrs参数中,我们就可以取出这些属性,对自定义View进行操作。
3.构造函数参数作用
xml中定义的属性会作为参数传入attrs中,见代码试例如下
这是xml文件:
<com.example.jimli.customviewtest.MyView
android:id="@+id/MyView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
ad:textSize="20sp"/>
然后我们观察attrs中的值
public MyView(Context context, AttributeSet attrs) { //在XML中定义会调用这个构造函数
super(context, attrs);
for (int i=0;i<attrs.getAttributeCount();i++){
Log.d("MyView",attrs.getAttributeName(i));
}
}
输出如下:
com.example.jimli.customviewtest D/MyView: id
com.example.jimli.customviewtest D/MyView: layout_width
com.example.jimli.customviewtest D/MyView: layout_height
com.example.jimli.customviewtest D/MyView: textSize
正好对应xml中的四个属性。这样一来,我们对attrs这个参数也就明白了,当在xml定义属性时,就会作为参数传入attrs属性。
public MyView(Context context, AttributeSet attrs, int defStyleAttr)
第三个参数的意义就如同它的名字所说的,是默认的Style,只是这里没有说清楚,这里的默认的Style是指它在当前Application或Activity所用的Theme中的默认Style。
为什么需要这个参数呢?因为当我们设置主题时,将会应用到所有的View上,为了也要方便我们的自定义View来使用这个主题属性,这就是这个参数的意义(纯属个人理解)
4.obtainStyledAttributes
public TypedArray obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
这个函数的四个参数我结合网上的一些文章来分析下我个人的理解
看网上的解释:
set:属性值的集合(也就是从Xml中传过来的属性值集合 见上面的输出)
attrs:我们要获取的属性的资源ID的一个数组,如同ContextProvider中请求数据库时的Projection数组,就是从一堆属性中我们希望查询什么属性的值
defStyleAttr:这个是当前Theme中的一个attribute,是指向style的一个引用,当在layout xml中和style中都没有为View指定属性时,会从Theme中这个attribute指向的Style中查找相应的属性值,这就是defStyle的意思,如果没有指定属性值,就用这个值,所以是默认值,但这个attribute要在Theme中指定,且是指向一个Style的引用,如果这个参数传入0表示不向Theme中搜索默认值
defStyleRes:这个也是指向一个Style的资源ID,但是仅在defStyleAttr为0或defStyleAttr不为0但Theme中没有为defStyleAttr属性赋值时起作用
链接中对这个函数说明勉强过得去,这里简要概括一下。对于一个属性可以在多个地方指定它的值,如XML直接定义,style,Theme,而这些位置定义的值有一个优先级,按优先级从高到低依次是:
直接在XML中定义>style定义>由defStyleAttr和defStyleRes指定的默认值>直接在Theme中指定的值
看这个关系式就比较明白了,defStyleAttr和defStyleRes在前面的参数说明中已经说了,“直接在Theme中指定的值”的意思在下面的示代码中可以看到。
下面来看看例子:
xml文件
<com.example.jimli.customviewtest.MyView
android:id="@+id/MyView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
ad:textSize="20sp"/>
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array=context.obtainStyledAttributes(attrs,R.styleable.MyView); //重点看这句
Log.i("MyView", "one:" + array.getString(R.styleable.MyView_textColor));
Log.i("MyView", "two:" + array.getString(R.styleable.MyView_textSize));
Log.i("MyView", "three:" + array.getString(R.styleable.MyView_textValue));
array.getString(R.styleable.MyView_textColor);
array.getString(R.styleable.MyView_textSize);
array.getString(R.styleable.MyView_textValue);
array.recycle();
}
重点看这句:
TypedArray array=context.obtainStyledAttributes(attrs,R.styleable.MyView);
R.styleable.MyView指定了我们想要获得什么数组,R.styleable.MyView可以见上面,我为了方便贴一下:
public static final class styleable {
public static final int[] MyView = {
0x7f0100b1, 0x7f0100b2, 0x7f0100b3
}; //想要获取的数组
/*下面为上面数组的索引*/
public static final int MyView_textColor = 0;
public static final int MyView_textSize = 1;
public static final int MyView_textValue = 2;
}
在Xml文件中我们指定了ad:textSize的值,所以优先从xml取出textSize的值,如果我们把 attrs置空 ,像这样
TypedArray array=context.obtainStyledAttributes(null,R.styleable.MyView);
这时候取出来的三个都为空。
这时候就要用到后面的俩个参数了:(int defStyleAttr, int defStyleRes)attrs没有值的时候就去默认主题找。