实现上述效果
代码实现如下:
package com.example.zhangdan.gesturedemo;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Vibrator;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
/**
* Created by zhangdan on 2018/1/26.
*/
public class GestureView extends View {
int height;
int width;
Canvas mCanvas;
private Paint mCirclePaint, mLinePaint;
private ArrayList<CircleRect> mCircleList;//圆心路径
private ArrayList<CircleRect> mLineList;//路径经过的圆心记录
private int redius;//半径
int specX, specY;// 屏幕寛高等分距离
private int normalColor = Color.GRAY; // 默认显示的颜色
private int selectColor = Color.BLUE; // 选中时显示的颜色
private int correctColor = Color.GREEN; // 正确时显示的颜色
private int wrongColor = Color.RED; // 错误时显示的颜色
float startX, startY;
Path mPath, tempPath;
Bitmap mBitmap;
boolean effectiveClick;
private final int STATE_NORMAL = 0;
private final int STATE_SELECTED = 2;
private final int STATE_CORRECT = 3;
private final int STATE_WRONG = 4;
StringBuffer pathsb;//记录路径中的圆点的下标
CallBackListener listener;
Vibrator vibrator;//震动
boolean isVibrator = true;//设置是否需要震动
Context context;
ResetRunnable resetRunnable;//手势完成后 重置
public GestureView(Context context) {
this(context, null);
}
public GestureView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public GestureView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
initView();
}
private void initView() {
mCirclePaint = new Paint();
mCirclePaint.setColor(normalColor);
//初始化画笔
mLinePaint = new Paint();
mLinePaint.setColor(selectColor);
mLinePaint.setAntiAlias(true);
mLinePaint.setDither(true);
mLinePaint.setStrokeWidth(10);
mLinePaint.setStrokeJoin(Paint.Join.ROUND);
mLinePaint.setStrokeCap(Paint.Cap.ROUND);
mLinePaint.setStyle(Paint.Style.STROKE);
mCircleList = new ArrayList<>();
mLineList = new ArrayList<>();
tempPath = new Path();//临时Path
mPath = new Path();
pathsb = new StringBuffer();
resetRunnable = new ResetRunnable();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.i(getClass().getSimpleName(), "onMeasure");
width = MeasureSpec.getSize(widthMeasureSpec);
height = MeasureSpec.getSize(heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Log.i(getClass().getSimpleName(), "onLayout");
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
if (width <= height) {
redius = width / 14;
specX = width / 4;//宽度等分四份
specY = height / 4;//高度等分四份
} else {
redius = height / 14;
specX = height / 4;
specY = width / 4;
}
mCircleList.clear();//放置数据重复加入
for (int i = 1; i <= 9; i++) {
int x = ((i - 1) % 3) * specX + specX;
int y = ((i - 1) / 3) * specY + specY;
CircleRect circleRect = new CircleRect(x, y);
circleRect.setIdnex(i);
mCircleList.add(circleRect);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.i(getClass().getSimpleName(), "onDraw");
canvas.drawBitmap(mBitmap, 0, 0, null);
for (int i = 0; i < mCircleList.size(); i++) {
drawRectCircle(mCircleList.get(i), mCircleList.get(i).getState());
}
canvas.drawPath(mPath, mLinePaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float curX = event.getX(), curY = event.getY();
CircleRect rect = getSelectedRect(curX, curY);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
this.reset();
if (rect != null) {
//获取圆心 保证路径通过圆心绘制
startY = rect.getY();
startX = rect.getX();
tempPath.moveTo(startX, startY);//此处的作用:让路径在 ACTION_MOVE 中开始绘制
mLineList.add(rect);
rect.setState(STATE_SELECTED);
effectiveClick = true;
}
break;
case MotionEvent.ACTION_MOVE:
if (effectiveClick) {
mPath.reset();
mPath.addPath(tempPath);
mPath.lineTo(startX, startY);
if (rect != null) {
startY = rect.getY();
startX = rect.getX();
mLineList.add(rect);
tempPath.lineTo(startX, startY);
rect.setState(STATE_SELECTED);
}
}
break;
case MotionEvent.ACTION_UP:
effectiveClick = false;
if (mLineList.size() > 0) {
mPath.reset();
mPath.addPath(tempPath);
pathsb.delete(0, pathsb.length());
for (CircleRect rect1 : mLineList) {
pathsb.append(rect1.getIdnex());
}
if (listener != null) {
if (listener.isSuccess(pathsb.toString())){
listener.onSuccess();
setAllViewState(STATE_CORRECT);
}else{
if (isVibrator){
if (vibrator==null){
vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
}
vibrator.vibrate(1000);
}
listener.onFailure();
setAllViewState(STATE_WRONG);
}
}
if (resetRunnable == null){
resetRunnable = new ResetRunnable();
}
this.postDelayed(resetRunnable,1000);
}
break;
}
invalidate();
return true;
}
public void setVibrator(boolean vibrator) {
isVibrator = vibrator;
}
public void setCallBackListener(CallBackListener l) {
this.listener = l;
}
/**
* 重置
*/
private void reset() {
setAllViewState(STATE_NORMAL);
mPath.reset();
tempPath.reset();
mLinePaint.setColor(selectColor);
mLineList.clear();
}
private void setAllViewState(int state) {
mLinePaint.setColor(getColorByState(state));
for (CircleRect rect : mLineList) {
rect.setState(state);
}
}
/**
* 根据状态绘制圆
*/
private void drawRectCircle(CircleRect rect, int state) {
mCirclePaint.setColor(getColorByState(state));
mCanvas.drawCircle(rect.getX(), rect.getY(), redius, mCirclePaint);
}
private int getColorByState(int state) {
switch (state) {
case STATE_NORMAL:
return normalColor;
case STATE_SELECTED:
return selectColor;
case STATE_CORRECT:
return correctColor;
case STATE_WRONG:
return wrongColor;
default:
return normalColor;
}
}
/*
判断哪个圆点被选中
*/
private CircleRect getSelectedRect(float x, float y) {
CircleRect rect = null;
for (int i = 0; i < mCircleList.size(); i++) {
rect = mCircleList.get(i);
if (Math.sqrt(Math.abs(x - rect.getX()) * Math.abs(x - rect.getX()) + Math.abs(y - rect.getY()) * Math.abs(y - rect.getY())) <= redius) {
if (rect.getState()!=STATE_SELECTED)//放置重复添加
return rect;
}
}
return null;
}
interface CallBackListener {
boolean isSuccess(String path);
void onSuccess( );
void onFailure( );
}
class ResetRunnable implements Runnable {
@Override
public void run() {
reset();//手势Up后1秒 重置状态
invalidate();
}
}
package com.example.zhangdan.gesturedemo;
/**
* Created by zhangdan on 2018/1/29.
*/
public class CircleRect {
int x;
int y;
int state;
int idnex;//记录路径点
public CircleRect(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public int getIdnex() {
return idnex;
}
public void setIdnex(int idnex) {
this.idnex = idnex;
}
}
需要注意:
1 震动需要<uses-permission android:name="android.permission.VIBRATE"/>
2 调用的时候需要调用setCallBackListener回调