序言:前两天因为项目的原因,去做了一下仿ios的数字解锁功能,然后写了那篇快给你的app上锁吧(android数字解锁),后来想到应用中常见的还有另外一种解锁就是绘制图案解锁,这两种解锁的布局看起来是很相似的,而且产生的结果也很相似,但是用户的操作不一样,下面我就给大家来说明一下

话不多说,先上图:

正常状态

按下状态

抬起错误状态

抬起正确状态

思路 这里又是一个九宫格布局,布局可以参考上一篇快给你的app上锁吧(android数字解锁),只不过这里的九宫格上我们画的是图片(bitmap)。onDraw方法中我们需要画两个东西,一个是点,另一个是线,画点我们就不多说了,根据坐标,将圆形图片画上去即可;

下面我们来看画线:

1、首先先要获得按下点的集合:
我们可以用集合来保存touch事件中按下的时候是九宫格中的点
复制代码
2、然后每两个点连成一条线
首先需要判断第一个点的状态是否是正常的(这个是点的属性,可以自定义),正常的话两点之间就连正确的线,错误的话两点之间就连错误的线
复制代码

布局画好之后我们还需要判断手势,即onTouch事件,按下,移动,抬起。

1、按下:
(1). 清空之前的操作,新一轮的绘制图案开始
(2). 检查当前按下的点与九宫格中的点是否吻合,如果吻合,将判断第一次是否选中九宫格中的点这个标识位置为true
复制代码
2、移动:
(1). 判断第一次按下是否选中九宫格中的点
(2). 如果第一次选中九宫格中的点,将手指在移动且手指按下的点不是九宫格中的点这个标识位置为true
复制代码
3、抬起:
(1). 将所有的标识位都还原成初始化
复制代码

绘制结束:

1、先判断绘制成不成立
2、然后根据绘制的结果向界面发送回调

至此,相关分析就结束了,详细的释义我会在代码中给出:

public class GraphicLockView extends View {private Point[][] points = new Point[3][3];  //创建一个3行3列的点数组private boolean isInit;   //判断有没有初始化private boolean isSelect;  //判断手指第一次按下屏幕有没有选中点private boolean isFinishMove;   //表示一次完整的图案绘制是否结束private boolean isMoveButNotPoint;   //表示手指在移动,但是并不是九宫格中的点private float width, height;   //屏幕宽高private static final int MIN_POINT = 4;    //最小能构成密码的点数private float offsetsX, offsetsY; //偏移量(在这里偏移量等于大边减去小边再除以2)private float bitmapR;   //图片资源的半径private float moveX, moveY;  //手势移动的x,y坐标private Bitmap bpPointNormal, bpPointPressed, bpPointError;  //点的三种图片private Bitmap bpLinePressed, bpLineError;  //线的三种图片private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);private List<Point> selectPointList = new ArrayList<>();   //储存按下的点的集合private Matrix matrix = new Matrix();  //矩阵,用来处理线的缩放private OnGraphicLockListener onGraphicLockListener;   //对外的监听器public GraphicLockView(Context context) {super(context);}public GraphicLockView(Context context, AttributeSet attrs) {super(context, attrs);}public GraphicLockView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}public void setOnGraphicLockListener(OnGraphicLockListener onGraphicLockListener) {this.onGraphicLockListener = onGraphicLockListener;}@Overrideprotected void onDraw(Canvas canvas) {//绘制之前要先初始化一下点,所以要先判断有没有初始化过if (!isInit) {//初始化点initPoints();}//绘制——将点绘制到画布上pointToCanvas(canvas);if (selectPointList.size() > 0) {Point startPoint = selectPointList.get(0);//绘制九宫格坐标里的点for (int i = 0; i < selectPointList.size(); i++) {Point endPoint = selectPointList.get(i);lineToCanvas(canvas, startPoint, endPoint);startPoint = endPoint;}//绘制九宫格坐标以外的点if (isMoveButNotPoint) {lineToCanvas(canvas, startPoint, new Point(moveX, moveY));}}}/*** 初始化点*/private void initPoints() {//1、先拿到画布的宽高(屏幕的宽高)width = getWidth();height = getHeight();/*================================================================================*///2、判断横竖屏并且计算偏移量if (width > height) {   //横屏//横屏时只有x坐标有偏移量offsetsX = (width - height) / 2;/*** 将手机屏幕可以看作是一个正方形(因为九宫格是正方形,在这里比较好计算),以最小边为基准*/width = height;} else {   //竖屏//竖屏时只有y坐标有偏移量offsetsY = (height - width) / 2;height = width;}/*================================================================================*///3、图片资源(图片资源自己加上)bpPointNormal = BitmapFactory.decodeResource(getResources(), R.drawable.point_normal);bpPointPressed = BitmapFactory.decodeResource(getResources(), R.drawable.point_pressed);bpPointError = BitmapFactory.decodeResource(getResources(), R.drawable.point_error);bpLinePressed = BitmapFactory.decodeResource(getResources(), R.drawable.line_pressed);bpLineError = BitmapFactory.decodeResource(getResources(), R.drawable.line_error);/*================================================================================*///4、点的坐标//第一排points[0][0] = new Point(offsetsX + width / 4, offsetsY + height / 4);points[0][1] = new Point(offsetsX + width / 2, offsetsY + height / 4);points[0][2] = new Point(offsetsX + width - width / 4, offsetsY + height / 4);//第二排points[1][0] = new Point(offsetsX + width / 4, offsetsY + height / 2);points[1][1] = new Point(offsetsX + width / 2, offsetsY + height / 2);points[1][2] = new Point(offsetsX + width - width / 4, offsetsY + height / 2);//第三排points[2][0] = new Point(offsetsX + width / 4, offsetsY + height - height / 4);points[2][1] = new Point(offsetsX + width / 2, offsetsY + height - height / 4);points[2][2] = new Point(offsetsX + width - width / 4, offsetsY + height - height / 4);/*================================================================================*///5、计算图片资源的半径bitmapR = bpPointNormal.getWidth() / 2;/*================================================================================*///6、设置密码按键,初始化每个点,设置为1——9int 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;}@Overridepublic boolean onTouchEvent(MotionEvent event) {moveX = event.getX();moveY = event.getY();isFinishMove = false;isMoveButNotPoint = false;Point point = null;switch (event.getAction()) {case MotionEvent.ACTION_DOWN://每次手指按下的时候都表示重新绘制图案resetPoint();//1、检查当前按下的点与九宫格中的九个点是否吻合point = checkSelectPoint();if (point != null) {isSelect = true;}break;case MotionEvent.ACTION_MOVE:if (isSelect) {point = checkSelectPoint();if (point == null) {isMoveButNotPoint = true;}}break;case MotionEvent.ACTION_UP:isFinishMove = true;isSelect = false;break;}//选中重复检查if (!isFinishMove && isSelect && point != null) {if (checkCrossPoint(point)) {  //交叉点isMoveButNotPoint = true;} else {   //非交叉点(新的点)point.status = Point.STATE_PRESSED;selectPointList.add(point);}}//绘制结束if (isFinishMove) {//绘制不成立if (selectPointList.size() == 1) {resetPoint();//绘制错误,点不够} else if (selectPointList.size() < MIN_POINT && selectPointList.size() > 0) {if (null != onGraphicLockListener) {onGraphicLockListener.setPwdFailure();}errorPoint();//绘制成功} else {if (null != onGraphicLockListener) {String strPassword = "";for (Point pwdPoint : selectPointList) {strPassword += pwdPoint.index;}if (!TextUtils.isEmpty(strPassword)) {onGraphicLockListener.setPwdSuccess(strPassword);}correctPoint();}}}//刷新view,会调用onDraw方法postInvalidate();return true;}/*** 检查交叉点** @param point 点* @return 是否交叉*/private boolean checkCrossPoint(Point point) {if (selectPointList.contains(point)) {return true;}return false;}/*** 设置绘制不成立*/public void resetPoint() {//将点的状态还原for (Point point : selectPointList) {point.status = Point.STATE_NORMAL;}selectPointList.clear();}/*** 设置绘制错误,将点的状态还原*/public void errorPoint() {for (Point point : selectPointList) {point.status = Point.STATE_ERROR;}new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}handler.sendEmptyMessage(0);}}).start();}/*** 设置绘制成功,将点的状态还原*/private void correctPoint() {new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}handler.sendEmptyMessage(0);}}).start();}private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {for (Point point : selectPointList) {point.status = Point.STATE_NORMAL;}selectPointList.clear();postInvalidate();}};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 (AppUtil.isCoincide(point.x, point.y, bitmapR, moveX, moveY)) {return point;}}}return null;}/*** 将点绘制到画布上** @param canvas 画布*/private void pointToCanvas(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 (points[i][j].status == Point.STATE_PRESSED) {canvas.drawBitmap(bpPointPressed, point.x - bitmapR, point.y - bitmapR, mPaint);} else if (points[i][j].status == Point.STATE_ERROR) {canvas.drawBitmap(bpPointError, point.x - bitmapR, point.y - bitmapR, mPaint);} else {canvas.drawBitmap(bpPointNormal, point.x - bitmapR, point.y - bitmapR, mPaint);}}}}/*** 将线绘制到画布上** @param canvas     画布* @param startPoint 开始的点* @param endPoint   结束的点*/private void lineToCanvas(Canvas canvas, Point startPoint, Point endPoint) {float lineLength = (float) AppUtil.twoPointDistance(startPoint, endPoint);float degree = AppUtil.getDegrees(startPoint, endPoint);canvas.rotate(degree, startPoint.x, startPoint.y);  //旋转if (startPoint.status == Point.STATE_PRESSED) {  //按下的状态//设置线的缩放比例,在这里线是往一个方向缩放的,即x轴,我们只需要设置x轴的缩放比例即可,y轴默认为1matrix.setScale(lineLength / bpLinePressed.getWidth(), 1);matrix.postTranslate(startPoint.x - bpLinePressed.getWidth() / 2, startPoint.y - bpLinePressed.getHeight() / 2);canvas.drawBitmap(bpLinePressed, matrix, mPaint);} else {   //错误的状态matrix.setScale(lineLength / bpLineError.getWidth(), 1);matrix.postTranslate(startPoint.x - bpLineError.getWidth() / 2, startPoint.y - bpLineError.getHeight() / 2);canvas.drawBitmap(bpLineError, matrix, mPaint);}canvas.rotate(-degree, startPoint.x, startPoint.y);  //把旋转的角度转回来}/*** 图案监听器*/public interface OnGraphicLockListener {void setPwdSuccess(String password);void setPwdFailure();}
}
复制代码

来看一波gif动图:

Github下载地址:传送门

快给你的app上锁吧(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. android图案解锁功能的实现

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

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

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

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

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

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

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

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

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

  8. Android 图案解锁

    首先还是看效果图. 图案解锁的功能在许多应用中都有用过,它比起数字解锁,带给用户的体验要好,今天就来一步一步实现这个功能. 一.初始化 初始化放在onDraw方法中,因为onDraw方法在绘制过程中会 ...

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

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

最新文章

  1. 在程序中支持多语言环境,支持asp.net和winform。
  2. 8.了解如何把vector和string数据传给旧的API
  3. xposed 修改参数_【Android 原创】2020春节红包第三题Xposed框架Hook的应用
  4. 在微信上如何查看2个人之间所有的红包转账记录,请注意是所有的?
  5. WinForm 窗体基本属性、公共控件
  6. 11.企业安全建设指南(金融行业安全架构与技术实践) --- 互联网应用安全
  7. matlab做拉普拉斯反演,拉普拉斯变换反演
  8. 基于JavaWeb的物流管理系统的设计与实现
  9. mysql更新数据的语句怎么写_mysql更新数据库语句怎么写?mysql更新字段语句
  10. My Firest FireMonkey App
  11. 用c语言合并两个有序单链表,c++ 如何合并两个有序链表
  12. 小卡机器人积木教程_这个会动的智能积木机器人分分钟让孩子爱不释手!秒杀家中99%的益智玩具!| 团...
  13. 超融合架构真的完美吗?
  14. PDF文档转换为图片、图片转成PDF 及PDF合并
  15. 蘑菇街Java后台面试总结
  16. getpid()函数和getppid()函数
  17. 阿里大数据分析展示工具DataV
  18. 计算机主板的定义,计算机主板前面板接口定义
  19. 国内十大正规贵金属交易软件排名(2022最新排行榜)
  20. 7-2 拼题A打卡奖励 dp

热门文章

  1. 《微机原理及接口技术》第03章在线测试
  2. 农业谋定投资-农业大健康·万祥军:解读胡春华补三农短板
  3. git 常用操作命令(Common operation)
  4. java基础—Hashtable,HashMap,TreeMap的差别
  5. Office HPDeskjetD2468 打印机电源灯闪烁不停,打印机不工作怎么办
  6. Delegate(QLabel和QComboBox)
  7. Client向Server send数据,返回WSAEWOULDBLOCK错误
  8. 使用jQuery Ajax功能的时候需要注意的一个问题
  9. 如何系统性的分析一个新idea的可行性?
  10. 【pmcaff】 会员原创讨论贴:互联网产品部-如此的羁绊