很多时候,Android自身提供给我们的视图可能不能满足我们的需求,这个时候我们就需要 自定义视图 。
虽然自定义视图很多,但总体归为两个类别:
- 简单视图 。简单视图内部当然也可以很复杂,之所以称之为简单视图是因为,简单视图中不包括子视图。简单视图几乎总是用来处理定制绘制。
- 聚合视图 。聚合视图一般包括很多个子视图,在聚合视图中一般不处理绘制事件,而是对子视图进行管理,绘制的任务一般都是有子视图来完成。
一般来说,自定义视图一般可以分为两步来完成。
- 选择合适的父类。 一般来说,如果我们想在某个控件的基础上进行延伸,一般选择该控件为父类进行自定义视图。如果我们向更加自由一下,那我们一般选择
View
作为我们的父类,继承View
相当于我们在一张空白的画纸上作画,所以更加自由,所以这种方法用的最多。 - 覆盖那些我们需要使用到的方法,在其中完成绘图操作。
接下来就开始我们的工作。
先新建一个 继承自 View
的 DrawingView
的类,并覆盖 View
的构造方法和 onDraw
方法。
package com.tobetheonlyone.startandroid;
import android.content.Context;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by ToBeTheOnlyOne on 2018/2/10.
*/
public class DrawingView extends View {
public DrawingView1(Context context, AttributeSet attrs){
super(context,attrs);
}
@Override
protected void onDraw(Canvas canvas){
}
}
先在 onDraw
方法中在绘制视图颜色为0xffdfdada
,并在视图中间绘制一个半经为 100
的圆。
@Override
protected void onDraw(Canvas canvas){
//绘制背景
canvas.drawColor(0xffdfdada);
//画笔的初始化
Paint drawPaint = new Paint();
drawPaint.setColor(0xffff0000);
//绘制圆形,前两个参数为圆点位置,第三个参数为圆的半径,第四个为使用的画笔
canvas.drawCircle(getWidth()/2,getHeight()/2,100,drawPaint);
}
运行效果如下:
现在基本的步骤我们都知道了,那如果我们自定义的视图要和用户交互该怎么办呢?其实也很简单,只要我们重写父类的 onTouchEvent
方法就可以,如下:
public class DrawingView extends View {
private final static String TAG = "DrawingView";
...
@Override
public boolean onTouchEvent(MotionEvent event) {
String action = " ";
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
action = "ACTION_DOWN";
break;
case MotionEvent.ACTION_UP:
action = "ACTION_UP";
break;
case MotionEvent.ACTION_MOVE:
action = "ACTION_MOVE";
break;
case MotionEvent.ACTION_CANCEL:
action = "ACTION_CANCEL";
break;
}
Log.w(TAG,action);
return true;
}
}
在 onTouchEvent
方法中我们对四个触摸动作进行了处理,即 ACTION_DOWN
, ACTION_UP
, ACTION_MOVE
, ACTION_CANCEL
这四个动作。这四个动作所处事件的阶段如下:
- ACTION_DOWN 手指触摸到屏幕
- ACTION_UP 手指离开屏幕
- ACTION_MOVE 手指在屏幕上移动
- ACTION_CANCEL 父视图拦截了触摸事件
再次运行项目,我们手指在屏幕上点击移动就可以在控制台看到响应事件。
接下来我们来实现一个简单的交互,即用户手指在屏幕上移动,DrawingView
在用户手指移动的区域画一个正方形。
首先,要知道定义矩形框的两个坐标点 : 原始坐标点(即手指的初始位置),当前坐标点(即手指当前的位置)。
因此,我们来新建一个 Rectangle 的类来保存我们矩形的属性:
Rectangle.class
public class Rectangle {
private PointF mOriginPoint;
private PointF mCurrentPoint;
public Rectangle(PointF origin){
mOriginPoint = origin;
mCurrentPoint = origin;
}
public PointF getCurrentPoint(){
return mCurrentPoint;
}
public void setCurrentPoint(PointF currentPoint){
mCurrentPoint = currentPoint;
}
public PointF getOriginPoint(){
return mOriginPoint;
}
}
修改我们的DrawingView
:
DrawingView.class
public class DrawingView extends View {
private Paint drawPaint;
private Rectangle mCurrentRect;
private List<Rectangle> mRectangleList = new ArrayList<>();
public DrawingView(Context context, AttributeSet attrs){
super(context,attrs);
//画笔的初始化
drawPaint = new Paint();
drawPaint.setColor(0x22ff0000);
}
@Override
protected void onDraw(Canvas canvas){
//绘制背景
canvas.drawColor(0xffdfdada);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
PointF current = new PointF(event.getX(),event.getY());
String action = " ";
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
action = "ACTION_DOWN";
mCurrentRect = new Rectangle(current);
mRectangleList.add(mCurrentRect);
break;
case MotionEvent.ACTION_UP:
action = "ACTION_UP";
mCurrentRect = null;
break;
case MotionEvent.ACTION_MOVE:
action = "ACTION_MOVE";
if(mCurrentRect!=null){
mCurrentRect.setCurrentPoint(current);
invalidate();
}
break;
case MotionEvent.ACTION_CANCEL:
action = "ACTION_CANCEL";
mCurrentRect = null;
break;
}
Log.w("DrawingView",action);
return true;
}
}
修改之后,无论在任何时候,视图接受到 ACTION_DOWN
动作事件,就以手指按下的点新建 Rectangle
对象并赋值给 mCurrentRect
,然后再将该对象添加到矩形框的列表中。
当用户手指在屏幕上移动时,会将手指所在的位置更新给 mCurrentRect.mCurrentPoint
,同时调用 invalidate()
方法强制 DrawingView
重新绘制自己,这样用户在屏幕上移动时就可以看到矩形框。
当取消触摸事件或者用户手指离开屏幕的时候,清空 mCurrentRect
以结束屏幕的绘制。
这些工作完成之后,运行项目,发现并没有出现绘制。这是因为我们并没有在 onDraw
方法中进行绘制,修改 onDraw
方法:
public class DrawingView extends View {
...
@Override
protected void onDraw(Canvas canvas){
//绘制背景
canvas.drawColor(0xffdfdada);
//绘制圆形
canvas.drawCircle(getWidth()/2,getHeight()/2,100,drawPaint);
for(Rectangle rectangle:mRectangleList){
float left = Math.min(rectangle.getOriginPoint().x,rectangle.getCurrentPoint().x);
float right = Math.max(rectangle.getOriginPoint().x,rectangle.getCurrentPoint().x);
float top = Math.min(rectangle.getOriginPoint().y,rectangle.getCurrentPoint().y);
float bottom = Math.max(rectangle.getOriginPoint().y,rectangle.getCurrentPoint().y);
canvas.drawRect(left,top,right,bottom,drawPaint);
}
}
...
}
修改之后再次运行,效果如下:
到此,我们自定义视图简单的探究就完成了。。。。