最近跟着视频学习了个图案解锁的功能,效果图如下:
需求如下:
1.第一次的需要设置锁屏密码
2.绘制密码的时候密码必须是五位或者五位以上的
3.绘制错误的时候会提示,并将点和线的状态还原成初始状态
4.绘制完成的时候,如果是五位及以上的,比对密码,如果一样则解锁成功,不一样就提示“密码错误”
具体操作如下
1.初始化点和线的资源
/**
* 初始化点
*/
private void initPoints() {
//1.获取布局宽高
width = getWidth();
height = getHeight();
//横屏和竖屏
if (width > height) {
offsetsX = (width - height) / 2;
width = height;
} else {
offssetsY = (height - width) / 2;
height = width;
}
//图片资源
pointNormal = BitmapFactory.decodeResource(getResources(), R.drawable.point_normal);
pointPressed = BitmapFactory.decodeResource(getResources(), R.drawable.point_pressed);
pointError = BitmapFactory.decodeResource(getResources(), R.drawable.point_error);
linePressed = BitmapFactory.decodeResource(getResources(), R.drawable.line_pressed);
lineError = BitmapFactory.decodeResource(getResources(), R.drawable.line_error);
points[0][0] = new Point((offsetsX + width / 4), (offssetsY + width / 4));
points[0][1] = new Point((offsetsX + width / 2), (offssetsY + width / 4));
points[0][2] = new Point((offsetsX + width - width / 4), (offssetsY + width / 4));
points[1][0] = new Point((offsetsX + width / 4), (offssetsY + width / 2));
points[1][1] = new Point((offsetsX + width / 2), (offssetsY + width / 2));
points[1][2] = new Point((offsetsX + width - width / 4), (offssetsY + width / 2));
points[2][0] = new Point((offsetsX + width / 4), (offssetsY + width - width / 4));
points[2][1] = new Point((offsetsX + width / 2), (offssetsY + width - width / 4));
points[2][2] = new Point((offsetsX + width - width / 4), (offssetsY + width - width / 4));
mPointRadius = pointNormal.getWidth() / 2;
// 设置密码
int index = 1;
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points[i].length; j++) {
points[i][j].index = index;
index++;
}
}
isInit = true;
}
2.绘制点
/**
* 将点绘制到画布
*
* @param canvas
*/
private void points2Canvas(Canvas canvas) {
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points[i].length; j++) {
Point point = points[i][j];
if (point.state == Point.STATE_NORMAL) {
canvas.drawBitmap(pointNormal, point.x - mPointRadius, point.y - mPointRadius, paint);
} else if (point.state == Point.STATE_PRESSED) {
canvas.drawBitmap(pointPressed, point.x - mPointRadius, point.y - mPointRadius, paint);
} else {
canvas.drawBitmap(pointError, point.x - mPointRadius, point.y - mPointRadius, paint);
}
}
}
}
3.movingX,movingY记录触发的位置,isFinish记录绘制图案是否结束,isSelected记录九宫格的点是否被选中,只要我们没有触发onTouchEvent方法中的 MotionEvent.ACTION_UP,就没有结束,只有手指抬起的时候才结束。
手指按下的时候需要判断当前按下的位置,是不是九宫格的点,在按下的时候,将所有的点和线还原
case MotionEvent.ACTION_DOWN:
//重新绘制
resetPoint();
point = checkSelectPoint();
if (point != null) {
isSelected = true;
}
break;
/**
* 检查点是否选中
*
* @return
*/
private Point checkSelectPoint() {
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points[i].length; j++) {
Point point = points[i][j];
if (with(point.x, point.y, mPointRadius, movingX, movingY)) {
return point;
}
}
}
return null;
}
/**
* 是否重合
*
* @param poinX 参考点的X
* @param pointY 参考点的Y
* @param r 圆的半径
* @param movingX 移动点的X
* @param movingY 移动点的Y
* @return 是否重合
*/
private static boolean with(float poinX, float pointY, float r, float movingX, float movingY) {
return Math.sqrt((poinX - movingX) * (poinX - movingX) + (pointY - movingY) * (pointY - movingY)) < r;
}
/**
* 重置
*/
public void resetPoint() {
//将点的状态还原
for (Point point : pointList) {
point.state = Point.STATE_NORMAL;
}
pointList.clear();
}
在手指移动的时候,如果点被选中,得到被选中的九宫格的点
if (isSelected) {
point = checkSelectPoint();
isMoveButNotPoint = true;
}
手指抬起的时候
isFinish = true;
isSelected = false;
在onTouchEvent的最后,绘制没有结束,点被选中,判断这个点是否在已选中的点的集合内,不在的话添加进去,在的话,移动但不是九宫格的点的标志isMoveButNotPoint为true
//选中重复检查
if (!isFinish && isSelected && point != null) {
if (checkCrossPoint(point)) { //交叉点
isMoveButNotPoint = true;
} else { //非交叉点(新的点)
point.state = Point.STATE_PRESSED;
pointList.add(point);
}
}
绘制结束,如果只有一个点则绘制不成立,绘制的点的个数 在2-5内,绘制错误,5个及以上表示成功,但不是真的成功,因为要和之前设置的点进行对比,一样才表示成功
//绘制结束
if (isFinish) {
if (pointList.size() == 1) {//绘制不成立
resetPoint();
} else if (pointList.size() < pointSize && pointList.size() >= 2) {//绘制错误
errPoint();
if (onPatterChangeListener != null) {
onPatterChangeListener.onPatterChange(null);
}
onResultRest();
} else {//绘制成功
if (onPatterChangeListener != null) {
String passwordStr = "";
for (int i = 0; i < pointList.size(); i++) {
passwordStr = passwordStr + pointList.get(i).index;
}
if (!TextUtils.isEmpty(passwordStr)) {
onPatterChangeListener.onPatterChange(passwordStr);
}
}
onResultRest();
}
}
绘制线
if (pointList.size() > 0) {
Point startPoint = pointList.get(0);
//绘制九宫格坐标里的点
for (int i = 0; i < pointList.size(); i++) {
Point endPoint = pointList.get(i);
lineToCanvas(canvas, startPoint, endPoint);
startPoint = endPoint;
}
//绘制九宫格坐标以外的点
if (isMoveButNotPoint) {
lineToCanvas(canvas, startPoint, new Point(movingX, movingY));
}
}
/**
* 将线绘制到画布上
*
* @param canvas 画布
* @param startPoint 开始的点
* @param endPoint 结束的点
*/
private void lineToCanvas(Canvas canvas, Point startPoint, Point endPoint) {
float lineLength = (float) twoPointDistance(startPoint, endPoint);
float degree = getDegrees(startPoint, endPoint);
canvas.rotate(degree, startPoint.x, startPoint.y); //旋转
if (startPoint.state == Point.STATE_PRESSED) { //按下的状态
//设置线的缩放比例,在这里线是往一个方向缩放的,即x轴,我们只需要设置x轴的缩放比例即可,y轴默认为1
matrix.setScale(lineLength / linePressed.getWidth(), 1);
matrix.postTranslate(startPoint.x - linePressed.getWidth() / 2, startPoint.y - linePressed.getHeight() / 2);
canvas.drawBitmap(linePressed, matrix, paint);
} else { //错误的状态
matrix.setScale(lineLength / lineError.getWidth(), 1);
matrix.postTranslate(startPoint.x - lineError.getWidth() / 2, startPoint.y - lineError.getHeight() / 2);
canvas.drawBitmap(lineError, matrix, paint);
}
canvas.rotate(-degree, startPoint.x, startPoint.y); //把旋转的角度转回来
}
点和线就绘制成功了,下面写个监听
/**
* 图案监听器
*/
public interface OnPatterChangeListener {
void onPatterChange(String passwordStr);
/**
* 图案重新绘制
*
* @param isStart
*/
void onPatterStart(boolean isStart);
}
private OnPatterChangeListener onPatterChangeListener;
public void setOnPatterChangeListener(OnPatterChangeListener onPatterChangeListener) {
this.onPatterChangeListener = onPatterChangeListener;
}
在绘制出错或者绘制成功的时候调用onPatterChange方法,在开始绘制的时候调用onPatterStart方法。
在绘制成功和绘制出错的时候将点和线的还原成初始状态
private void onResultRest() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
resetPoint();
postInvalidate();
}
}, 1000);
}
至此,给各位大佬奉上项目地址:
https://github.com/diudiuhf/SatelliteMenu