图案锁屏

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

图案锁屏.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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容