首先还是看效果图。

图案解锁的功能在许多应用中都有用过,它比起数字解锁,带给用户的体验要好,今天就来一步一步实现这个功能。

一、初始化

初始化放在onDraw方法中,因为onDraw方法在绘制过程中会执行多次,我们设置一个标量isInit,使初始化只执行一次。

@Overrideprotected void onDraw(Canvas canvas) {if (!isInit) {init();}}private void init() {pressPaint.setColor(Color.YELLOW);pressPaint.setStrokeWidth(10);errorPaint.setColor(Color.RED);errorPaint.setStrokeWidth(10);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);normalBitap = BitmapFactory.decodeResource(getResources(), R.drawable.normal);pressBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.press);errorBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.error);bitmapR = normalBitap.getWidth() / 2;isInit = true;}

二、绘制九个点

绘制前我们先需要确定这九个点的位置,为了适应手机屏的横竖屏切换,我们把这九个点的位置控制在屏幕最短长度范围内,也就是竖屏时我们以手机宽度为准,横屏时我们以手机的高度为准。然后以这个长度确定一个正方形区域,在这个正方形区域内画出这九个点并均匀分布,点的位置已在上面的初始化工作中计算好了,下面就直接绘制了。

private void drawPoint(Canvas canvas) {for (int i = 0; i < points.length; i++) {for (int j = 0; j < points[1].length; j++) {Point point = points[i][j];if (point.state == Point.STATE_NORMAL) {canvas.drawBitmap(normalBitap, point.x - bitmapR, point.y - bitmapR, pointPaint);} else if (point.state == Point.STATE_PRESS) {canvas.drawBitmap(pressBitmap, point.x - bitmapR, point.y - bitmapR, pointPaint);} else {canvas.drawBitmap(errorBitmap, point.x - bitmapR, point.y - bitmapR, pointPaint);}}}}

这里需要注意画笔绘制的起始点和点所在的中心点有所偏差,所以代码中我们横纵坐标都减去了点的半径长度(bitmapR)。

三、记录手指的绘制

@Overridepublic boolean onTouchEvent(MotionEvent event) {
//        super.onTouchEvent(event);mouseX = event.getX();mouseY = event.getY();int[] ij;int i, j;switch (event.getAction()) {case MotionEvent.ACTION_DOWN:resetPoint();ij = getSelectPoint();if (ij != null) {isDraw = true;i = ij[0];j = ij[1];points[i][j].state = Point.STATE_PRESS;pressedPoints.add(points[i][j]);passList.add(i * points.length + j);}break;case MotionEvent.ACTION_MOVE:if (isDraw) {ij = getSelectPoint();if (ij != null) {i = ij[0];j = ij[1];//同一个点不能再添加到pressedPoints中if (!pressedPoints.contains(points[i][j])) {points[i][j].state = Point.STATE_PRESS;pressedPoints.add(points[i][j]);passList.add(i * points.length + j);}}}break;case MotionEvent.ACTION_UP:isDraw = false;break;}invalidate();return true;}

每次重新绘制前都重置所有数据

public void resetPoint() {pressedPoints.clear();passList.clear();for (int i = 0; i < points.length; i++) {for (int j = 0; j < points[0].length; j++) {points[i][j].state = Point.STATE_NORMAL;}}invalidate();}

判断手指划过的地方是否是图案中的某个点

private int[] getSelectPoint() {Point pMouse = new Point(mouseX, mouseY);System.out.println("bitmapR:" + bitmapR);for (int i = 0; i < points.length; i++) {for (int j = 0; j < points[0].length; j++) {if (points[i][j].distance(pMouse) < bitmapR) {int[] result = new int[2];result[0] = i;result[1] = j;return result;}}}return null;}

返回的数组中记录点的坐标位置

手指滑动过程中,还需要不断的画线,此时onDraw方法是这样的:

@Overrideprotected void onDraw(Canvas canvas) {if (!isInit) {init();}//画点drawPoint(canvas);//有点后,开始画线if (pressedPoints.size() > 0) {Point a = pressedPoints.get(0);for (int i = 0; i < pressedPoints.size(); i++) {Point b = pressedPoints.get(i);drawLine(canvas, a, b);a = b;}if (isDraw) {drawLine(canvas, a, new Point(mouseX, mouseY));}}}

画线的方法:

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 interface OnDrawFinishedListener {boolean onDrawFinished(List<Integer> passList);}private OnDrawFinishedListener onDrawFinishedListener;public void setOnDrawFinishedListener(OnDrawFinishedListener onDrawFinishedListener) {this.onDrawFinishedListener = onDrawFinishedListener;}

我们在手势抬起时调用这个监听方法,onTouchEvent(MotionEvent event)方法中:

case MotionEvent.ACTION_UP:boolean valid = false;if (onDrawFinishedListener != null && isDraw) {valid = onDrawFinishedListener.onDrawFinished(passList);
//                    onDrawFinishedListener.onDrawFinished(passList);}
//                if (passList.size() <= 3) {//                    valid = false;
//                }else {//                    valid = true;
//                }if (!valid) {for (Point p : pressedPoints) {p.state = Point.STATE_ERROR;}}isDraw = false;break;

到这里就完成了图案解锁的自定义View。

在Activity中使用

GestureView myView = (GestureView) findViewById(R.id.view);myView.setOnDrawFinishedListener(new GestureView.OnDrawFinishedListener() {@Overridepublic boolean onDrawFinished(List<Integer> passList) {boolean flag = false;if (passList.size() <= 3) {Toast.makeText(GestureActivity.this, "图案绘制有误!", Toast.LENGTH_SHORT).show();flag = false;}else {Toast.makeText(GestureActivity.this, "图案绘制完成!", Toast.LENGTH_SHORT).show();flag = true;}return flag;}});

最后附上GestureView的完整代码。

public class GestureView extends View {private boolean isInit; //是否初始化private Point[][] points = new Point[3][3];private Paint pointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);private Paint pressPaint = new Paint();private Paint errorPaint = new Paint();private Bitmap normalBitap, pressBitmap, errorBitmap;private int bitmapR;private boolean isDraw;private ArrayList<Point> pressedPoints = new ArrayList<>();private ArrayList<Integer> passList = new ArrayList<>(); //记录所滑过的点的位置,第几个点,以便后续验证public GestureView(Context context, AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onDraw(Canvas canvas) {if (!isInit) {init();}//画点drawPoint(canvas);//有点后,开始画线if (pressedPoints.size() > 0) {Point a = pressedPoints.get(0);for (int i = 0; i < pressedPoints.size(); i++) {Point b = pressedPoints.get(i);drawLine(canvas, a, b);a = b;}if (isDraw) {drawLine(canvas, a, new Point(mouseX, mouseY));}}}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);}}private void drawPoint(Canvas canvas) {for (int i = 0; i < points.length; i++) {for (int j = 0; j < points[1].length; j++) {Point point = points[i][j];if (point.state == Point.STATE_NORMAL) {canvas.drawBitmap(normalBitap, point.x - bitmapR, point.y - bitmapR, pointPaint);} else if (point.state == Point.STATE_PRESS) {canvas.drawBitmap(pressBitmap, point.x - bitmapR, point.y - bitmapR, pointPaint);} else {canvas.drawBitmap(errorBitmap, point.x - bitmapR, point.y - bitmapR, pointPaint);}}}}public void resetPoint() {pressedPoints.clear();passList.clear();for (int i = 0; i < points.length; i++) {for (int j = 0; j < points[0].length; j++) {points[i][j].state = Point.STATE_NORMAL;}}invalidate();}private void init() {pressPaint.setColor(Color.YELLOW);pressPaint.setStrokeWidth(10);errorPaint.setColor(Color.RED);errorPaint.setStrokeWidth(10);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);normalBitap = BitmapFactory.decodeResource(getResources(), R.drawable.normal);pressBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.press);errorBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.error);bitmapR = normalBitap.getWidth() / 2;isInit = true;}float mouseX, mouseY;@Overridepublic boolean onTouchEvent(MotionEvent event) {
//        super.onTouchEvent(event);mouseX = event.getX();mouseY = event.getY();int[] ij;int i, j;switch (event.getAction()) {case MotionEvent.ACTION_DOWN:resetPoint();ij = getSelectPoint();if (ij != null) {isDraw = true;i = ij[0];j = ij[1];points[i][j].state = Point.STATE_PRESS;pressedPoints.add(points[i][j]);passList.add(i * points.length + j);}break;case MotionEvent.ACTION_MOVE:if (isDraw) {ij = getSelectPoint();if (ij != null) {i = ij[0];j = ij[1];if (!pressedPoints.contains(points[i][j])) {points[i][j].state = Point.STATE_PRESS;pressedPoints.add(points[i][j]);passList.add(i * points.length + j);}}}break;case MotionEvent.ACTION_UP:boolean valid = false;if (onDrawFinishedListener != null && isDraw) {valid = onDrawFinishedListener.onDrawFinished(passList);
//                    onDrawFinishedListener.onDrawFinished(passList);}
//                if (passList.size() <= 3) {//                    valid = false;
//                }else {//                    valid = true;
//                }if (!valid) {for (Point p : pressedPoints) {p.state = Point.STATE_ERROR;}}isDraw = false;break;}invalidate();return true;}private int[] getSelectPoint() {Point pMouse = new Point(mouseX, mouseY);for (int i = 0; i < points.length; i++) {for (int j = 0; j < points[0].length; j++) {if (points[i][j].distance(pMouse) < bitmapR) {int[] result = new int[2];result[0] = i;result[1] = j;return result;}}}return null;}public interface OnDrawFinishedListener {boolean onDrawFinished(List<Integer> passList);}private OnDrawFinishedListener onDrawFinishedListener;public void setOnDrawFinishedListener(OnDrawFinishedListener onDrawFinishedListener) {this.onDrawFinishedListener = onDrawFinishedListener;}
}

欢迎关注公众号。

Android 图案解锁相关推荐

  1. android 解锁图案代码,Android图案解锁code.docx

    Android图案解锁code Main_Acitivity.javapackage com.example.lackpatternview;import android.os.Bundle;impo ...

  2. Flex【原创】模拟Android图案解锁

    Flex 实现Android图案解锁功能 看见Andorid系统里面有图案解锁的功能,试想能用Flex的移动开发实现吗?答案是:完全可以! 环境:Flex 4.6(air3.2) 先看我的包结构: 第 ...

  3. 快给你的app上锁吧(android图案解锁)

    序言:前两天因为项目的原因,去做了一下仿ios的数字解锁功能,然后写了那篇快给你的app上锁吧(android数字解锁),后来想到应用中常见的还有另外一种解锁就是绘制图案解锁,这两种解锁的布局看起来是 ...

  4. Android 图案解锁 9宫格密码解锁

    序言  第一次写Android技术博客,不知道该如何下手. 背景  现在人们越来越重视自己的隐私,对于一些涉及用户隐私的应用,用户可能会希望在应用启动时必须先输入密码.传统的数字式密码记忆繁琐.容易破 ...

  5. android图案解锁功能的实现

    我们经常会在app中看到图案解锁的功能,所以寻思做一个,在某客视频上看到了教程,自己跟着做了一遍,记录一下,顺便理清一下思路. 思路讲解: 首先自定义一个图案的view,其中实现onDraw方法,以及 ...

  6. android 图案解锁忘记了,安卓手机忘记图形解锁、锁屏密码的解决方法

    Android 手机的图形解锁倒是真的好用了,主要是方便新颖,并且便于记忆,自从有了图形解锁,很多人都不再使用密码屏幕锁了,图形解锁倒是好玩,但是经常换来换去的话就会造成一时间想不起哪个图形解锁图案才 ...

  7. android图案解锁忘了怎么解,安卓手机解锁图案忘了怎么办?手机解锁密码忘了的解决办法...

    现在的智能手机解锁方法有很多,有的朋友喜欢几天换一个密码.但是有的人就会把自己设的手机解锁图案密码忘记了,今天小编就带着大家一起解决这个问题,赶紧和小编一起看看吧 一共有三种办法: 1.双清(恢复出厂 ...

  8. android图案解锁忘了怎么解,手机图案解锁忘了怎么办 三种方法轻松解决【图文】...

    随着智能机的普及,手机上锁方法也有了新的方式,除了以前传统的密码锁之外,手机还添加了极富趣味的图案锁.九宫格形式的图案锁屏,看上去不仅新颖,而且锁屏更加方面,这种锁屏很快就受到不少用户的喜爱!可是在使 ...

  9. android 图案解锁密码,关于Android手机图案解锁总密码数,个人写的一个递归版求解...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 没有注释,测试运行一下就行了,大部分人还是能看懂的. import java.util.Vector; public class TestLock { p ...

最新文章

  1. Spring Cloud Kubernetes 指南
  2. python一个月能挣多少钱-零基础学python,我可以让你一个月上手做项目!
  3. 提升win双屏体验_海信双屏A6L评测,在自由阅读中植入护眼水墨屏
  4. Flink Forward Global 2021 议题征集ing!
  5. mysql数据库-mysql数据定义语言DDL (Data Definition Language)归类(六)
  6. 人生历练必备的十个心态(图)
  7. [org.hibernate.util.JDBCExceptionReporter] - Cannot load JDBC driver class 'net.
  8. java页面渲染_史上最详细的页面渲染机制
  9. 常见的7种深度学习框架对比
  10. Android Ptrace Inject
  11. 小学生机器人挑战赛_厉害了我的小学生!德阳中小学生机器人大赛
  12. 一位硕士毕业生三个月求职经历与经验的结晶
  13. S7-300系列PLC如何通过GSD文件实现PROFIBUS DP主从通讯?
  14. Excel如何对多分隔符号数据进行分列
  15. python关键词排名批量查排名_Python 批量获取Baidu关键词的排名并入库
  16. ubuntu20.04 常用开发工具整理
  17. 第1章思维导图图片版
  18. 如何实现小程序的无限推送
  19. android开机默认打开指定程序,android 开机默认进入指定Launcher
  20. Linux根目录下各目录的作用

热门文章

  1. SpringBoot_整合SpringSecurity(前后端分离版)
  2. 江苏2021年新高考八省联考成绩查询,江苏2021八省联考分数、位次汇总!
  3. 美宇航局下一个挑战项目:高科技飞艇
  4. 2023年第一砖——IT市场的变和不变
  5. 广州未成年子女落户政策需要什么申请条件和申办材料
  6. [转帖]浪潮信息最大供应商英特尔(Intel):2018 年采购额 145.76 亿元
  7. 百度地图展示附近1公里范围内的目标
  8. android天气预报获取-气象台webservice
  9. 动力登山绳 EN 892标准
  10. 华为云大数据智能数据湖FusionInsight