系统自带的View可以在xml中配置属性,对于自定义View同样可以在xml中配置属性,为了使自定义的View的属性可以在xml中配置,需要以下4个步骤:
1、通过
2、在xml中为相应的属性声明属性值
3、在运行时(一般为构造函数)获取属性值
4、将获取到的属性值应用到自定义View
一般自定义View有一个构造方法:
public class MView extends View{
public MView(Context context) ;
public MView(Context context, @Nullable AttributeSet attrs);
public MView(Context context, @Nullable AttributeSet attrs, int defStyleAttr);
public MView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) ;
}
第1个构造方法:只有一个context参数,一般用于代码中new出一个控件,它不带任何属性参数。
第2个构造方法:加载你在layout.xml文件中配置控件时调用,它有相对应的属性,例如:layout_width、background等
第3个构造方法:在第2个构造方法的基础上,额外添加一个属性,这个属性指向一个默认style
第4个构造方法:在第3个构造方法的基础上,额外添加一个style。注意:如果defStyleAttr有效(defStyleAttr不为0或者有定义defStyleAttr),则defStyleRes无效;如果defStyleAttr无效,则defStyleRes有效。defStyleAttr的优先级比defStyleRes高。
属性优先级(1>2>3>4):
1、直接在layout.xml文件中添加的属性
2、在style中添加的属性
3、defStyleAttr或defStyleRes(二者不会同时有效)
4、theme中添加的属性
通过
上述attrs.xml文件中,声明了属性集合gui和属性attr11。系统会在R.attr(R文件路径:app\build\generated\not_namespaced_r_class_sources\debug\r\包名\R.java)中生成相应的属性(R文件是在Build时候自动)
public static final class attr {
......
public static final int attr1=0x7f03002b;
public static final int attr11=0x7f03002c;
public static final int attr2=0x7f03002d;
public static final int attr3=0x7f03002e;
public static final int attr4=0x7f03002f;
public static final int attr5=0x7f030030;
public static final int attr6=0x7f030031;
public static final int attr7=0x7f030032;
......
}
不同的是,如果声明在declare-styleable中,系统还会为我们在R.styleable中生成相关的属性
public static final class styleable {
......
public static final int[] gui={
0x7f03002b, 0x7f03002d, 0x7f03002e, 0x7f03002f,
0x7f030030, 0x7f030031, 0x7f030032
};
public static final int gui_attr1=0;
public static final int gui_attr2=1;
public static final int gui_attr3=2;
public static final int gui_attr4=3;
public static final int gui_attr5=4;
public static final int gui_attr6=5;
public static final int gui_attr7=6;
......
}
如上所述,R.styable.gui是一个int[],里面元素的值正好和R.attr里的属性值一样。gui_attrX表示的是attrX在R.styleable.gui里面的索引。
在xml中添加属性和属性值的几种方式:
1、直接在layout文件中添加
2、设置style并在style中添加
3、Application和Activity可以指定theme,可以在theme中添加
直接在layout中添加
在style中添加
在theme中添加
在上述xml文件中,我们在AppTheme中添加了attr3和attr11,attr11是一个reference,指向ThemeStyle,在ThemeStyle中,我们添加了attr4属性。在theme中设置属性有两种方式:1、直接在theme中添加一个属性(例如attr4),2、定义一个reference类型属性并指向另外一个style。这两种方式是有区别的,它们的属性优先级不一样。
package com.gui.gui.custom.view.component;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import com.gui.gui.custom.view.R;
import androidx.annotation.Nullable;
public class TestView1 extends View {
public TestView1(Context context) {
this(context, null);
}
public TestView1(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, R.attr.attr11);
}
public TestView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, R.style.DefStyleRes);
}
public TestView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.gui, defStyleAttr, defStyleRes);
log("TypedArray length:" + ta.length());
for (int i = 0; i < ta.length(); i++) {
int attrIndex = ta.getIndex(i);
switch (attrIndex) {
case R.styleable.gui_attr1:
log(ta.getString(attrIndex));
break;
case R.styleable.gui_attr2:
log(ta.getString(attrIndex));
break;
case R.styleable.gui_attr3:
log(ta.getString(attrIndex));
break;
case R.styleable.gui_attr4:
log(ta.getString(attrIndex));
break;
case R.styleable.gui_attr5:
log(ta.getString(attrIndex));
break;
case R.styleable.gui_attr6:
log(ta.getString(attrIndex));
break;
case R.styleable.gui_attr7:
log(ta.getString(attrIndex));
break;
default:
break;
}
}
ta.recycle();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
private void log(String msg) {
Log.v(getClass().getSimpleName(), "" + msg);
}
}
context.obtainStyledAttributes方法接受四个参数,其中Attributes、defAttr、defStyle都属于告诉程序从哪取属性,第2个参数
R.styleable.gui表示取哪些属性参数。
运行结果:
2020-01-22 15:03:22.133 14297-14297/com.gui.gui.custom.view V/TestView1: TypedArray length:7
2020-01-22 15:03:22.133 14297-14297/com.gui.gui.custom.view V/TestView1: attr1:layout.xml中申明
2020-01-22 15:03:22.133 14297-14297/com.gui.gui.custom.view V/TestView1: attr2:在style中申明
2020-01-22 15:03:22.134 14297-14297/com.gui.gui.custom.view V/TestView1: attr3:在Theme Style中申明
2020-01-22 15:03:22.134 14297-14297/com.gui.gui.custom.view V/TestView1: attr4:在Theme Style中申明
2020-01-22 15:03:22.135 14297-14297/com.gui.gui.custom.view V/TestView1: attr5:在Theme中申明
现在我们对第3个构造方法加以修改,是defStyleAttr无效
public TestView1(Context context, @Nullable AttributeSet attrs) {
//this(context, attrs, R.attr.attr11);
// 改成如下参数
this(context, attrs, 0);
}
运行结果如下:
2020-01-22 15:08:04.188 14486-14486/com.gui.gui.custom.view V/TestView1: TypedArray length:7
2020-01-22 15:08:04.188 14486-14486/com.gui.gui.custom.view V/TestView1: attr1:layout.xml中申明
2020-01-22 15:08:04.188 14486-14486/com.gui.gui.custom.view V/TestView1: attr2:在style中申明
2020-01-22 15:08:04.189 14486-14486/com.gui.gui.custom.view V/TestView1: attr3:在def style res中申明
2020-01-22 15:08:04.189 14486-14486/com.gui.gui.custom.view V/TestView1: attr4:在def style res中申明
2020-01-22 15:08:04.190 14486-14486/com.gui.gui.custom.view V/TestView1: attr5:在Theme中申明