快给你的app上锁吧(android图案解锁)
时间:2022-06-06
本文章向大家介绍快给你的app上锁吧(android图案解锁),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
序言:前两天因为项目的原因,去做了一下仿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;
}
@Override protected 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——9
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;
}
@Override public 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() {
@Override public void run() { try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(0);
}
}).start();
} /**
* 设置绘制成功,将点的状态还原
*/
private void correctPoint() { new Thread(new Runnable() {
@Override public void run() { try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(0);
}
}).start();
} private Handler handler = new Handler() {
@Override public 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轴默认为1
matrix.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下载地址:传送门
- 数据库静默安装总结(r3笔记第58天)
- 用TensorFlow实现文本分析模型,做个聊天机器人
- 深度学习:用tensorflow建立线性回归模型
- 用python基于2015-2016年的NBA常规赛及季后赛的统计数据分析
- 数值信息的机器级存储
- ABAP和Java里关于DEFAULT(默认)机制的一些语言特性
- Golang语言社区--golang 进度下载文件
- Golang语言社区--Go语言基础第七节函数调用等
- Hyperledger也能实现Token代币
- 经典Java面试题收集(二)
- 关于表联结方法(一)(r3笔记第57天)
- Go 语言读写 Excel 文档
- 关于索引的使用模式(r3笔记56天)
- 关于oracle中的半连接(r3笔记55天)
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- PHP 之变量
- 在网页中常用到的几种居中方法
- 秋招面经三(作业帮、新浪、阿里云)
- 初识HTML
- iOS 点击按钮复制文本
- AntUI滑块Sliders
- AntUI卡片Cards
- AntUI常规Forms表单
- 二叉树:看看这些树的最大深度
- C++核心准则SF.5: .cpp文件必须包含定义它接口的.h文件
- C++核心准则SF.6:(只)为转换,基础库或在局部作用域内部使用using namspace指令
- C++核心准则SF.7:不要在头文件中的全局作用域中使用using namespace指令
- 二叉树:看看这些树的最小深度
- Hive初体验
- Hive数据的存储以及在centos7下进行Mysql的安装