要实现自定义View,这里有两种情况:
如果所有基本控件都无法满足你的需求,需要实现完全自定义,继承View;
如果仅仅是在已存基本控件基础上稍作调整,可以简单的继承目标控件,并覆盖某些方法。
1) 对于第一种情况,实现的基本步骤如下:
1. 最为常见的能够被继承的,毫不惊奇的说是View类,因此,你通常会从继承View开始。
2. 你可以提供这样一个构造函数,用来从XML文件中获取属性值和参数值,而且你可以使用自定义的属性和参数。
3. 你可能需要创建自己的事件监听器,如属性值的访问和更改。
4. 你当然需要覆盖onMeasure()方法,如果需要显示组件,也需要覆盖onDraw()方法。当然,对于这两个被重写的父类中的函数来说,onDraw()是一个空的函数,什么也不做,而onMeasure()默认设置大小为100*100.
5. 其他一些onXXXX()方法可能也需要被覆盖。
2) 对于第二种情况,实现的基本步骤如下:
1. 继承View或者其某个子类
2. 覆盖父类的方法。父类中那些需要覆盖的方法以‘on’开头,如onDraw(),onMeasure(),onKeyDown()等。
3. 使用你的View
MyView.java是一个简单的自定义View,onDraw()方法实现了对圆形的绘制。
/**
* @author cappuccino
* @date 2013-2-25下午5:45:43
*/
package com.cappuccino.layout;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
/**
* 自定义View
* @author cappuccino
* @date 2013-2-25下午5:45:43
*/
public class MyView extends View {
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint pt = new Paint();
pt.setColor(Color.RED);
pt.setAntiAlias(true);
canvas.drawCircle(20, 20, 100, pt);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Activity中调用的代码片段如下:
如果作为布局:
setContentView(new MyView(this));
如果View作为组件:
RelativeLayout = (RelativeLayout) findViewById(R.id.RelativeLayout);
view = (MyView) new MyView(this);
RelativeLayout.addView(view);
1) 对于重写onDraw()和onMeasure()的说明
onDraw()方法提供给你Canvas对象,你可以做些2D图形的绘制,如果需要实现3D图形的绘制,你不应该继承View,取而代之的是继承SurfaceView,并且使用单独的线程去完成。查看GLSurfaceViewActivity了解详细信息。
onMeasure()函数有点复杂,它是绘制过程中组件与容器交互的关键。OnMeasure()方法被重载,以至于可以高效而精确的测量被包含的每一个部分。这变得稍微有点复杂的是,因为我们需要考虑来自父View的限制(通过onMeasure()方法传递过来),而且在width和height被测量后,立即调用setMeasureDimension()方法被设置进去。如果在重写onMeasure()方法时,没有调用setMeasureDimension()这个函数,在测量过程中会抛出异常。
总的来说,重写onMeasure()方法分为以下几点:
1. onMeasure()方法的传入参数是width和height的测量规格(widthMeasureSpec和heightMeasureSpec参数都是整数型的),这两个参数用来限制你的width和height尺寸。
2. 你的组件的onMeasure()方法将会测量出width和height尺寸来渲染目标组件。它会保持与传入进来的规格参数一致,尽管可以选择超过这个规格(这种情况下,父Vie能够选择做些什么,包括修改,滑动,抛出异常,或者要求onMeasure()使用不同的规格参数再次处理)。
3. 一旦width和height被测量出来,setMeasureDimension(int width,int height)方法必须被调用,否则会导致异常。
l 自定义组合控件
有些时候仅仅是将基础组件进行简单的组合就可以达到我们的目的,比如ComboBox、Spinner、AutoCompleteTextView。为了创建自己的组合组件,下面是一些主要的步骤:
1. 通常有一个布局开始,因此首先继承一个Layout。在Combox中,需要一个水平布局的LinearLayout。记住其他的布局是可以嵌套进去的,因此组合起来的组件可以是任意的组合和结构。注意到正如一个Activity那样,可以通过XML文件来申明你的组件,也可以使用代码。
2. 在新建的View的构造函数中,获取父类需要的参数,传入父类的构造函数,接着,你可以设置其他需要用到的View的属性。注意的是,在XML中你也可以定义自己的View属性或参数。例如,某个列表项被点击选中时的事件监听,它需要立即更新EditText的内容以响应之前的点击事件。
3. 你可以建立自己的事件监听器。
4. 你也可以创建自己的属性以及获取和改变它们的方法。例如,允许组件中的EditText初始化时显示初始值,并且需要的时候查询它的内容。
5. 在继承Layout的情况下,你不需要去重载onDraw()和onMeasure()方法,因为Layout默认的方法能够工作的很好。当然,如果你觉得有必要也可以重载它们。
6. 如果可能,你可以重载一些onXXXX()方法。比如onKeyDown(),可以实现当某个按键被按下时,comboBox中的某个值默认被选中。
总的来说,将Layout作为建立自定义组件的基类有如下优点:
1. 可以像Activity那样利用XML文件来定义或者使用代码将它们嵌入进去。
2. 不需要重载onDraw()和onMeasure()方法,因为它们已经做的很好了。
3. 最后,你可以非常快速的建立复杂的组合Views,并且像单一组件那样复用。