绘制完成后,提供给外部的回调
setOnDrawFinishedListener(OnDrawFinishedListener listener) 方法
OnDrawFinishedListener 接口
重置点的方法,供外部调用
resetPoints() 方法
具体代码如下:
自定义控件:GestureLock
package com.example.zhh.shoushimima3;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义图案解锁控件
*/
public class GestureLock extends View {
// 存放点对象的数组
private Point[][] points = new Point[3][3];
// 判断是否初始化,onDraw方法会被多次调用,我只要初始化一次
private boolean inited = false;
// 三种状态下点对应的Bitmap;用于绘制图片
private Bitmap bitmapPointError;
private Bitmap bitmapPointNormal;
private Bitmap bitmapPointPress;
// 手指点的x,y
float mouseX, mouseY;
// 圆圈的半径
private float bitmapR;
// 画笔,用来绘制图片
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
// 画笔,绘制正确的线
Paint pressPaint = new Paint();
// 画笔,绘制错误的线
Paint errorPaint = new Paint();
// 标记当前是否在绘制状态
private boolean isDraw = false;
// 选中点的集合
private ArrayList<Point> pointList = new ArrayList<Point>();
// 存储点的位置,是0到8的整数值
private ArrayList<Integer> passList = new ArrayList<Integer>();
private OnDrawFinishedListener listener;
/**
* 构造方法
*
* @param context
*/
public GestureLock(Context context) {
super(context);
}
public GestureLock(Context context, AttributeSet attrs) {
super(context, attrs);
}
public GestureLock(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 绘制
*
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 初始化(只能在这里初始化)
if (!inited) {
init();
}
// 绘制点
drawPoints(canvas);
// 绘制线
// 判断有点时,绘制
if (pointList.size() > 0) {
Point a = pointList.get(0);
for (int i = 1; i < pointList.size(); i++) {
Point b = pointList.get(i);
// 绘制a到b线
drawLine(canvas, a, b);
a = b;
}
// 最后一个点,和手指按下点的连线
if (isDraw) {
drawLine(canvas, a, new Point(mouseX, mouseY));
}
}
}
/**
* 初始化方法
*/
private void init() {
pressPaint.setColor(Color.YELLOW);
pressPaint.setStrokeWidth(5);
errorPaint.setColor(Color.RED);
errorPaint.setStrokeWidth(5);
// 初始化三种状态对应的位图
bitmapPointError = BitmapFactory.decodeResource(getResources(), R.drawable.error);
bitmapPointNormal = BitmapFactory.decodeResource(getResources(), R.drawable.normal);
bitmapPointPress = BitmapFactory.decodeResource(getResources(), R.drawable.press);
// 计算出图片的半径
bitmapR = bitmapPointError.getHeight() / 2;
// 手机屏幕的宽
int width = getWidth();
// 手机屏幕的高度
int height = getHeight();
// 整个九宫格的偏移量,保证横竖配屏都在中间,求得是宽高差的绝对值,这是个公式
int offset = Math.abs(width - height) / 2;
// 定义横纵坐标的偏移量
int offsetX, offsetY;
// 小方格的边长
int space;
if (width > height) {
// 横屏状态
space = height / 4;
offsetX = offset;
offsetY = 0;
} else {
// 竖屏状态
space = width / 4;
offsetX = 0;
offsetY = offset;
}
// 实例化点对象,并设置点位置,并存入数组
points[0][0] = new Point(offsetX + space, offsetY + space);
points[0][1] = new Point(offsetX + space * 2, offsetY + space);
points[0][2] = new Point(offsetX + space * 3, offsetY + space);
points[1][0] = new Point(offsetX + space, offsetY + space * 2);
points[1][1] = new Point(offsetX + space * 2, offsetY + space * 2);
points[1][2] = new Point(offsetX + space * 3, offsetY + space * 2);
points[2][0] = new Point(offsetX + space, offsetY + space * 3);
points[2][1] = new Point(offsetX + space * 2, offsetY + space * 3);
points[2][2] = new Point(offsetX + space * 3, offsetY + space * 3);
// 防止重复初始化
inited = true;
}
/**
* 绘制点
*
* @param canvas
*/
private void drawPoints(Canvas canvas) {
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points[i].length; j++) {
if (points[i][j].state == Point.STATE_NORMAL) {
//Normal 正常状态 绘制图片
canvas.drawBitmap(bitmapPointNormal, points[i][j].x - bitmapR, points[i][j].y - bitmapR, paint);
} else if (points[i][j].state == Point.STATE_PRESS) {
//Press 按下状态 绘制图片
canvas.drawBitmap(bitmapPointPress, points[i][j].x - bitmapR, points[i][j].y - bitmapR, paint);
} else {
//ERROR 错误状态 绘制图片
canvas.drawBitmap(bitmapPointError, points[i][j].x - bitmapR, points[i][j].y - bitmapR, paint);
}
}
}
}
/**
* 判断选择的点
* 就是手指点下的地方属于哪个点
*
* @return
*/
private int[] getSelectedPoint() {
Point pMouse = new Point(mouseX, mouseY);
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points[i].length; j++) {
// 根据按的位置,判断是否属于这个点,
// 按下的位置的距离小于半径,认为按得就是这个点
// 按下位置的距离距圆心,小于半径,认为点的就是这个点
if (points[i][j].distance(pMouse) < bitmapR) {
// 选中点在数组中的位置
int[] result = new int[2];
result[0] = i;
result[1] = j;
return result;
}
}
}
return null;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mouseX = event.getX();
mouseY = event.getY();
int[] ij;
int i, j;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 将所有得点,设置为初始状态
resetPoints();
ij = getSelectedPoint();
// 不是空,说明有选中的点
if (ij != null) {
// 标记为绘制状态
isDraw = true;
i = ij[0];
j = ij[1];
// 选中点标记为按下状态
points[i][j].state = Point.STATE_PRESS;
// 把选中的点放在集合中,就是起点
pointList.add(points[i][j]);
// 二维数组,转化一维数组的公式
passList.add(i * 3 + j);
}
break;
case MotionEvent.ACTION_MOVE:
// 判断处于绘制状态
if (isDraw) {
ij = getSelectedPoint();
if (ij != null) {
// 不是空,说明手指又碰到了一个点
i = ij[0];
j = ij[1];
// 判断选中的点是否在已经在集合中,如果没有就添加
// 每个点之后能使用一次
if (!pointList.contains(points[i][j])) {
points[i][j].state = Point.STATE_PRESS;
pointList.add(points[i][j]);
// 二位数组,转化成一维数组的公式
passList.add(i * 3 + j);
}
}
}
break;
case MotionEvent.ACTION_UP:
// 是否绘制正确
boolean valid = false;
// 判断是否在绘制状态
if (listener != null && isDraw) {
valid = listener.OnDrawFinished(passList);
}
// 绘制错误
if (!valid) {
for (Point p : pointList) {
p.state = Point.STATE_ERROR;
}
}
// 不在绘制
isDraw = false;
break;
}
this.postInvalidate();
return true;
}
/**
* 绘制连线
*
* @param canvas
* @param a
* @param b
*/
private void drawLine(Canvas canvas, Point a, Point b) {
// 判断第一个手否为按下状态
if (a.state == Point.STATE_PRESS) {
canvas.drawLine(a.x, a.y, b.x, b.y, pressPaint);
} else if (a.state == Point.STATE_ERROR) {
canvas.drawLine(a.x, a.y, b.x, b.y, errorPaint);
}
}
/**
* 复位
* 设置点的初始状态
* 手滑动一次后,重新绘制
* 重新绘制点
*/
public void resetPoints() {
// 清空集合
passList.clear();
// 清空选中点的集合
pointList.clear();
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points[i].length; j++) {
// 把所有的点的状态设置为,未点击之前的状态
points[i][j].state = Point.STATE_NORMAL;
}
}
// 重绘
this.postInvalidate();
}
/**
* 暴露在外部回调的方法
* 绘制完成的方法
* @param listener
*/
public void setOnDrawFinishedListener(OnDrawFinishedListener listener) {
this.listener = listener;
}
/**
* 绘制完成的监听
*/
public interface OnDrawFinishedListener {
// 绘制图案是否正确
// true 正确
// false 错误
boolean OnDrawFinished(List<Integer> passList);
}
}
实体类:Point
package com.example.zhh.shoushimima3;
/**
* Created by 泽群 on 2015/6/20.
* 保存9个点
*/
public class Point {
// 正常状态
public static int STATE_NORMAL = 0;
// 按下状态
public static int STATE_PRESS = 1;
// 错误状态
public static int STATE_ERROR = 2;
// 当前点的x值
float x;
// 当前点的y值
float y;
// 保存当前点的状态
int state = STATE_NORMAL;
// 构造函数,构造点
public Point(float x, float y) {
this.x = x;
this.y = y;
}
/**
* 计算距离的方法
* @param a
* @return
*/
public float distance(Point a) {
// 计算距离的公式
float distance = (float) Math.sqrt((x - a.x) * (x - a.x) + (y - a.y) * (y - a.y));
return distance;
}
}
activity_main.xml中引用
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.zhh.shoushimima3.GestureLock
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</com.example.zhh.shoushimima3.GestureLock>
</RelativeLayout>
源码下载:
https://download.csdn.net/download/zhaihaohao1/10667418
参考视频:
https://www.jikexueyuan.com/course/1592.html