图案锁屏

最近跟着视频学习了个图案解锁的功能,效果图如下:

图案锁屏.gif

需求如下:
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

你可能感兴趣的:(图案锁屏)