开发一个名为BoxDrawingView的定制View子类,响应用户的触摸与拖动,定制View将在屏幕上绘制出矩形框。效果如下:
BoxDrawingView.java
package com.example.draganddraw;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* 尽管有着各式各样的定制视图,但仍可硬性将它们分为两大类别。 1、简单视图。简单视图可以有复杂的内部;之所以归为简单类别,是因为简单视图不包括子视图。
* 而且,简单视图几乎总是会执行定制绘制。 2、聚合视图。聚合视图由一些其他视图对象组成。聚合视图通常管理着子视图,但不负则执行定制绘制。
* 相反,图形绘制任务都委托给了各子视图。
*
* 以下为创建定制视图所需的三大步骤:
* 1、选择超类。对于简单定制视图而言,View是一个空白画布,因此是最常见的选择。而对于聚合定制视图,我们应该选择合适的布局类。
* 2、继承选定的超类,并至少覆盖一个超类构造方法。或者创建自己的构造方法,并在其中调用超类的构造方法。 3、覆盖其它关键方法,以定制视图行为。
*/
public class BoxDrawingView extends View {
private Box mCurrentBox;
private ArrayList<Box> mBoxs = new ArrayList<Box>();
private Paint mBoxPaint;
private Paint mBackgoundPaint;
/**
* 这里之所以添加两个构造方法,是因为视图可从代码或者布局文件实例化。从布局文件中实例化的视图可收到一个AttributeSet的实例,
* 该实例包含了XML布局文件中指定的XML属性。即使不打算使用构造方法,按习惯做法,我们也应该添加它们。
*/
public BoxDrawingView(Context context) {
this(context, null);
}
public BoxDrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
mBoxPaint = new Paint();
mBoxPaint.setColor(0x22ff0000);
mBackgoundPaint = new Paint();
mBackgoundPaint.setColor(0xfff7efe0);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPaint(mBackgoundPaint);
for (Box box : mBoxs) {
float left = Math.min(box.getOrigin().x, box.getCurrent().x);
float right = Math.max(box.getOrigin().x, box.getCurrent().x);
float top = Math.min(box.getOrigin().y, box.getCurrent().y);
float bottom = Math.max(box.getOrigin().y, box.getCurrent().y);
canvas.drawRect(left, top, right, bottom, mBoxPaint);
}
}
/**
* 该方法接受一个MotionEvent类实例,而MotionEvent类可用来描述包括位置和动作的触摸事件。动作则用来描述事件所处的阶段。
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
PointF curr = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mCurrentBox = new Box(curr);
mBoxs.add(mCurrentBox);
break;
case MotionEvent.ACTION_MOVE:
if(mCurrentBox != null){
mCurrentBox.setCurrent(curr);
invalidate();//该方法强制BoxDrawingView重新绘制自己。
}
break;
case MotionEvent.ACTION_UP:
mCurrentBox = null;
break;
case MotionEvent.ACTION_CANCEL:
mCurrentBox = null;
break;
}
return true;
}
}
MotionEvent动作常量
动作常量 | 动作描述 |
---|---|
ACTION_DOWN | 用户手指触摸到屏幕 |
ACTION_MOVE | 用户在屏幕上移动手指 |
ACTION_UP | 用户手指离开屏幕 |
ACTION_CANCEL | 父视图拦截了触摸事件 |