自定义控件常见的几种方式:
Android View类是所有view的超类。
* 自定义类直接继承View或者ViewGroup。
* 这种方式需要自己去处理AT_MOST的情况。
* 自定义类继承已有的View,例如Button。
* 系统已经帮你处理好AT_MOST的情况,开发人员直接在其基础上去添加新功能即可。
* 自定义组合控件。
* 将需要的布局转化为一个ViewGroup去管理
*自定义类去继承该布局的根元素,例如LinearLayout,RelativeLayout等ViewGroup
自定义的View属性(建立View的内置属性)
步骤
- 在values/attrs.xml建立创建自定义属性。
- 在XML文件中指定属性的值。
- 在自定义类中获取属性的值。
- 将获取到的属性值应用到自定义View中。
创建自定义属性
创建values/attrs.xml文件,添加如下代码即可。
<?xml version="1.0" encoding="utf-8"?>
<!-- string,color,dimension,integer,enum,reference,float,boolean,fraction,flag; -->
<resources>
<declare-styleable name="CustomView">
<attr name="textColor" format="color" />
<attr name="textSize" format="dimension" />
<attr name="name" format="string" />
<attr name="isShow" format="boolean" />
<attr name="Oriental" format="enum">
<enum name="Horizontal" value="1"></enum>
<enum name="Vertical" value="0"></enum>
</attr>
</declare-styleable>
</resources>
可以将一些公共的属性抽取到外面,那么多个自定义控件都可公用这个属性了
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="textColor" format="color" />
<declare-styleable name="CustomView">
<attr name="textColor" />
</declare-styleable>
</resources>
使用自定义属性
- 自定义的属性与系统提供的属性用法有一点不一样,那就是命名空间不一样。
- 系统属性的namespace:
xmlns:android="http://schemas.android.com/apk/res/android"
- 我们需要给自定义的View也加上namespace
xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews"
或者是
xmlns:custom="http://schemas.android.com/apk/res-auto"(推荐使用)
完整例子
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.example.view.CustomView
android:layout_width="160dip"
android:layout_height="wrap_content"
custom:name="123456789"
custom:Oriental="Horizontal"
custom:textSize="15sp" />
</RelativeLayout>
在自定义类中获取属性值
- 在自定义类中必须有带有AttributeSet参数的构造函数,这样是为了方便获取通过布局文件实例化的自定义View对象。
- 一般情况下的模版写法(相互调用,最终都由第三个构造去处理)
public CustomView(Context context) {
this(context, null);
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
从AttributeSet中直接获取属性值
int attributeCount = attrs.getAttributeCount();
for(int i = 0; i<attributeCount;i++) {
String name = attrs.getAttributeName(i);
String value = attrs.getAttributeValue(i);
Log.d("CustomView", name+"..."+value);
}
04-12 10:37:45.357: D/CustomView(27360): paddingLeft...10.0dip
04-12 10:37:45.358: D/CustomView(27360): paddingRight...10.0dip
04-12 10:37:45.358: D/CustomView(27360): layout_width...160.0dip
04-12 10:37:45.358: D/CustomView(27360): layout_height...-2
04-12 10:37:45.358: D/CustomView(27360): textSize...15.0sp
04-12 10:37:45.358: D/CustomView(27360): name...123456789
04-12 10:37:45.358: D/CustomView(27360): Oriental...1
结果可以看到获取到的是所有的属性
* 根据官方文档介绍,虽然可以直接从AttributeSet中直接获取到属性值,但是有两个弊端
* Resource references within attribute values are not resolved
* Styles are not applied
google建议将将AttributeSet传递给obtainStyledAttributes()中
* 该方法会返回一个TypeArray对象。
* 已经dereferenced and styled。
* 系统在之前就已经做了很多准备工作,只等你去调用obtainStyledAttributes()方法
* 在R.java文件中已经定义自定义属性的ids值。
* 在Array中为每一个属性定义了index去表示指定的定义属性。
* 开发者只需要拿预定义的常量值去获取属性即可。
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// 得到自定义属性的值
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.CustomView, defStyle, 0);
int count = a.getIndexCount();
for (int i = 0; i < count; i++) {
int index = a.getIndex(i);//得到表示属性值的常量标记
switch (index) {
case R.styleable.CustomView_name:
mName = a.getString(index);
break;
case R.styleable.CustomView_textSize:
mTextSize = a.getDimensionPixelSize(index, 16);
break;
default:
break;
}
}
// 回收
a.recycle();
}
- 注意:TypeArray是一个share resource使用后必须记得关闭回收。
提供可供外界触发的事件
- 为什么要暴露属性给外界?
- 为了让view具备动态的效果,就得通过修改View中的某一些属性,从而从新去绘制和布局View达到动态的效果。也就是去调用invalidate或者postInvalidate方法去进行view的重绘。
- 怎么暴露?
- getter/setter方法。
- 暴露什么属性比较适合?
- 规则就是暴露影响自定义View外观和行为的属性。
- getter/setter方法一般会通过某些事件去触发执行,例如监控手势滑动事件不断修改View属性。