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

来源:互联网 发布:七天网络查分下载 编辑:程序博客网 时间:2024/04/30 08:58

序言:前两天因为项目的原因,去做了一下仿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下载地址:传送门

这是我建的一个android小白的群,各位有兴趣的小白欢迎加群共同学习,也欢迎各位大神进群指导,共勉。群号:541144061

0 0
原创粉丝点击