Lock your app (android pattern unlock)

Keywords: Android github iOS Attribute

Preface: Two days ago, because of the project, I did a digital unlocking function imitating ios, and then I wrote that article. Lock your app (android digital unlock) Later, I came to think that there is another common unlock in the application, which is to draw pattern unlock. The layout of the two unlocks looks very similar, and the results are very similar, but the user's operation is different. Let me explain to you below.

Let's not say much. Firstly, the picture above:

Normal condition

Press state

Lift up the wrong state

Lift the correct state

thinking
Here is another Nine-palace layout, which can be referred to in the previous article. Lock your app (android digital unlock) It's just that we're drawing bitmaps on the nine palaces here. In the onDraw method, we need to draw two things, one is a point, the other is a line. We will not say much about drawing points. According to coordinates, we can draw a circular picture.

Now let's draw the line:

1. First, we need to get the set of pressing points:
We can use collections to save the point in the nine palaces when the touch event is pressed.
2. Then each two points are connected into a line.
First of all, we need to determine whether the state of the first point is normal (this is the attribute of the point, which can be customized). If it is normal, there will be a correct line between the two points, and if it is wrong, there will be a wrong line between the two points.

After the layout is drawn, we also need to judge the gesture, that is, the onTouch event, press, move and lift.

1. Press:
(1) Operation before clearing, a new round of drawing pattern begins
 (2) Check whether the point currently pressed coincides with the point in the Nine Palaces. If the coincidence occurs, it will be judged whether the point in the Nine Palaces is selected for the first time.
2. Mobile:
(1) Judging whether the point in the Nine Palaces is selected by the first press
 (2) If the point in the Nine Palace is selected for the first time, the mark position of the point that the finger is moving and the point that the finger is pressing is not the point in the Nine Palace is true.
3. Lift up:
(1). Restore all identification bits to initialization

End of drawing:

1. Judging whether the drawing is valid first
2. Then send callbacks to the interface based on the results of the drawing.

At this point, the relevant analysis is over, and I will give a detailed explanation in the code:

public class GraphicLockView extends View {

    private Point[][] points = new Point[3][3];  //Create a point array of three rows and three columns

    private boolean isInit;   //Determine whether there is initialization

    private boolean isSelect;  //Determine if the finger first clicks on the screen has a selected point

    private boolean isFinishMove;   //Represents the end of a complete pattern drawing

    private boolean isMoveButNotPoint;   //It means that the fingers are moving, but it's not the point in the ninth grid.

    private float width, height;   //Screen width and height

    private static final int MIN_POINT = 4;    //Minimum number of points that can constitute a password

    private float offsetsX, offsetsY; //Offset (where the offset is equal to the large side minus the small side and divides by 2)

    private float bitmapR;   //Radius of Picture Resources

    private float moveX, moveY;  //x,y coordinates of gesture movement

    private Bitmap bpPointNormal, bpPointPressed, bpPointError;  //Three Pictures of Points

    private Bitmap bpLinePressed, bpLineError;  //Three Pictures of Line

    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    private List<Point> selectPointList = new ArrayList<>();   //Store the set of pressed points

    private Matrix matrix = new Matrix();  //Matrix to handle line scaling

    private OnGraphicLockListener onGraphicLockListener;   //External Monitor

    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) {
        //Initialize a point before drawing, so you must first determine whether it has been initialized or not.
        if (!isInit) {
            //Initialization point
            initPoints();
        }
        //Drawing - Drawing points onto canvas
        pointToCanvas(canvas);

        if (selectPointList.size() > 0) {
            Point startPoint = selectPointList.get(0);
            //Draw the points in the nine-grid coordinates
            for (int i = 0; i < selectPointList.size(); i++) {
                Point endPoint = selectPointList.get(i);
                lineToCanvas(canvas, startPoint, endPoint);
                startPoint = endPoint;
            }
            //Drawing Points Beyond Nine-Palace Coordinates
            if (isMoveButNotPoint) {
                lineToCanvas(canvas, startPoint, new Point(moveX, moveY));
            }
        }
    }

    /**
     * Initialization point
     */
    private void initPoints() {
        //1. Get the width and height of the canvas first (the width and height of the screen)
        width = getWidth();
        height = getHeight();

        /*================================================================================*/

        //2. Judging the horizontal and vertical screen and calculating the offset
        if (width > height) {   //Horizontal screen
            //Only x coordinates have offset in horizontal screen
            offsetsX = (width - height) / 2;
            /**
             * Think of the screen as a square (because the nine palaces are square, which is better to calculate here), with the smallest side as the benchmark.
             */
            width = height;
        } else {   //Vertical screen
            //When the screen is vertical, only y coordinates have offset.
            offsetsY = (height - width) / 2;
            height = width;
        }

        /*================================================================================*/

        //3. Picture resources (Picture resources added by themselves)
        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. Point coordinates
        //First row
        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);

        //The second row
        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);

        //The third row
        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. Calculating the radius of image resources
        bitmapR = bpPointNormal.getWidth() / 2;

        /*================================================================================*/

        //6. Set password keys, initialize each point, set to 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++;
            }
        }
        /*================================================================================*/

        //Initialization complete
        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:
                //Every time a finger is pressed, it means redrawing the pattern.
                resetPoint();
                //1. Check whether the points currently pressed coincide with the nine points in the Nine Palaces.
                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;
        }
        //Repeated checks in selection
        if (!isFinishMove && isSelect && point != null) {
            if (checkCrossPoint(point)) {  //Intersection
                isMoveButNotPoint = true;
            } else {   //Non-intersection (new point)
                point.status = Point.STATE_PRESSED;
                selectPointList.add(point);
            }
        }
        //End of drawing
        if (isFinishMove) {
            //Drawing does not hold water
            if (selectPointList.size() == 1) {
                resetPoint();
                //Drawing errors, not enough points
            } else if (selectPointList.size() < MIN_POINT && selectPointList.size() > 0) {
                if (null != onGraphicLockListener) {
                    onGraphicLockListener.setPwdFailure();
                }
                errorPoint();
                //Successful rendering
            } else {
                if (null != onGraphicLockListener) {
                    String strPassword = "";
                    for (Point pwdPoint : selectPointList) {
                        strPassword += pwdPoint.index;
                    }
                    if (!TextUtils.isEmpty(strPassword)) {
                        onGraphicLockListener.setPwdSuccess(strPassword);
                    }
                    correctPoint();
                }
            }
        }
        //Refreshing view calls onDraw method
        postInvalidate();
        return true;
    }

    /**
     * Check the intersection
     *
     * @param point spot
     * @return Is it cross?
     */
    private boolean checkCrossPoint(Point point) {
        if (selectPointList.contains(point)) {
            return true;
        }
        return false;
    }

    /**
     * Settings rendering is not valid
     */
    public void resetPoint() {
        //Restore the state of a point
        for (Point point : selectPointList) {
            point.status = Point.STATE_NORMAL;
        }
        selectPointList.clear();
    }

    /**
     * Set the drawing error to restore the state of the point
     */
    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();
    }

    /**
     * Set the drawing to succeed and restore the state of the point
     */
    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;
    }

    /**
     * Draw points onto the canvas
     *
     * @param canvas canvas
     */
    private void pointToCanvas(Canvas canvas) {
        //The Set of Ergodic Points
        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);
                }
            }
        }
    }

    /**
     * Draw the line onto the canvas
     *
     * @param canvas     canvas
     * @param startPoint Starting point
     * @param endPoint   The end point
     */
    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);  //rotate
        if (startPoint.status == Point.STATE_PRESSED) {  //Pressed state
            //Set the zoom ratio of the line, where the line is zoomed in one direction, that is, the x-axis, we only need to set the zoom ratio of the x-axis, the y-axis defaults to 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 {   //The state of error
            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);  //Turn the angle of rotation back.
    }

    /**
     * Pattern monitor
     */
    public interface OnGraphicLockListener {
        void setPwdSuccess(String password);

        void setPwdFailure();
    }
}

Let's look at a wave of gif motions:

Github Download Address: Portal

This is an android Xiaobai group I built. Interested Xiaobai welcomes you to join the group to learn together, and you are also welcome to join us in guiding and encouraging. Group number: 541144061

Posted by Amy1980 on Fri, 22 Mar 2019 05:33:52 -0700