备注说明:通过之前的“Android图案解锁”学习,特意将实现的思路给理出来、记录下来,作为自己的学习总结。文中存在不对之处,请大家踊跃指出,转载请标明出处。

实现思路:

1、自定义LockPatterView控件,定义九宫格点的类型,实现绘制九宫格布局。

2、重写onTouchEvent()事件,完成选中点的绘制。

3、绘制两点之间的连接线。

4、图案密码的设置与验证。

效果展示:

Android13 解锁界面 android 设置解锁界面_图案密码

一、自定义LockPatterView控件,定义九宫格点的类型,实现绘制九宫格布局。

1、定义九宫格中的点类型。

/**自定义九宫格点类型*/
    public class Point{
        public static final int STATE_NORMAL=0;//正常时
        public static final int STATE_PRESS=1;//选中时
        public static final int STATE_ERROR=2;//错误时
        public float x,y;//点的坐标
        public int state;//点的状态
        public Point(float x,float y){
            this.x=x;
            this.y=y;
        }
    }

2、初始化九宫格的点坐标

/**初始化点的数据*/
    private void initPoints() {
        //1.得到控件的布局
        float width=getWidth();
        float height=getHeight();
        //2.得到横竖屏时的偏移量
        if(width>height){//横屏时,高度充满整个控件
            offsetX=(width-height)/2;//宽度居于控件中央显示
            offsetY=0;
            width=height;
        }else{//竖屏时,宽度充满整个控件的宽度
            offsetX=0;
            offsetY=(width-height)/2;//高度居于控件中央显示
            height=width;
        }
         //图片资源
        bitmap_normal = BitmapFactory.decodeResource(getResources(), R.drawable.ic_point_normal);
        bitmap_pressed = BitmapFactory.decodeResource(getResources(), R.drawable.ic_point_press);
        bitmap_error = BitmapFactory.decodeResource(getResources(),  R.drawable.ic_point_error);
        bitmap_line = BitmapFactory.decodeResource(getResources(),  R.drawable.ic_line_normal);
        bitmap_line_error = BitmapFactory.decodeResource(getResources(),  R.drawable.ic_line_error);
        //3.初始化九宫格点的坐标
        for(int i=0;i<points.length;i++){
            for(int j=0;j<points[i].length;j++){
                 points[i][j]=new Point(width/4+j*width/4+offsetX,height/4+height/4*i+offsetY);
            }
        }
        isInit=true;
    }

3、将九宫格点绘制到画布上

/**将点画在画布上*/
    private void points2Canvas(Canvas canvas) {
        // TODO Auto-generated method stub
        //3.绘制九宫格点
        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(bitmap_normal, point.x-bitmap_normal.getWidth()/2, point.y-bitmap_normal.getHeight()/2, paint);
                }else if(point.state==Point.STATE_PRESS){
                    canvas.drawBitmap(bitmap_pressed, point.x-bitmap_pressed.getWidth()/2, point.y-bitmap_pressed.getHeight()/2, paint);
                }else{
                    canvas.drawBitmap(bitmap_error, point.x-bitmap_error.getWidth()/2, point.y-bitmap_error.getHeight()/2, paint);      
                }
            }
        }
    }

4、最终效果

Android13 解锁界面 android 设置解锁界面_图案密码_02

二、重写onTouchEvent()事件,完成选中点的绘制。

1、判断移动的点是否属于九宫格的点,如果是,则该点属于选中的点。

/**判断两点是否重合*/
        public static boolean with(float pointX,float pointY,float r,float moveX,float moveY){
            return Math.sqrt((pointX-moveX)*(pointX-moveX)+(pointY-moveY)*(pointY-moveY))<r;
        }
        /**检查移动的点是否属于九宫格的点*/
    private Point checkPoints(){
        for(int i=0;i<points.length;i++){
            for(int j=0;j<points[i].length;j++){
                Point point=points[i][j];
                if(Point.with(point.x, point.y, bitmap_normal.getWidth()/2, moveX, moveY)){
                    return point;
                }
            }
        }
        return null;
    }

2、判断该点是否属于交叉点(之前已经添加过的点)。

/**检查是否是交叉点*/
    private boolean crossPoint(Point point){
        return pointList.contains(point);
    }

3、完成选中点的添加。

private float moveX,moveY;//移动点的坐标
    private boolean isSelect,isFinish;
    private List<Point>pointList=new ArrayList<LockPatterView.Point>();
    private static final int mMinPointCount=5;//最少绘制点数
@Override
    public boolean onTouchEvent(MotionEvent event) {
        moveX=event.getX();
        moveY=event.getY();
        isFinish=false;
        Point point=null;
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            resetPoints();
            point=checkPoints();
            if(point!=null){
                isSelect=true;
            }
            break;
        case MotionEvent.ACTION_MOVE:

            break;
        case MotionEvent.ACTION_UP:
            isFinish=true;
            isSelect=false;
            break;
        }
        //添加点
        if(!isFinish&&isSelect&&point!=null){
            if(crossPoint(point)){//是交叉点

            }else{//新点
                point.state=Point.STATE_PRESS;
                pointList.add(point);
            }
        }
        //绘制结束
        if(isFinish){
            if(pointList.size()==1){//只绘制一个点
                resetPoints();
            }else if(pointList.size()<mMinPointCount){//小于5个点时
                errorPoints();
            }else{//正确的点,用于之后作为密码使用

            }
        }
        postInvalidate();//通知onDraw()方法,重新绘制
        return true;//触摸事件能够继续执行下去
    }

4、最终效果。

Android13 解锁界面 android 设置解锁界面_Android13 解锁界面_03

三、绘制两点之间的连线。

1、设置连接线的缩放。

/**判断两点之间的距离*/
        public static double distance(Point a,Point b){
            return Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
        }
            //得到线的长度
        float linelength=(float) Point.distance(a, b);
if(a.state==Point.STATE_PRESS){
            //设置线的缩放
              matrix.setScale(linelength / bitmap_line.getWidth(), 1);
              //设置线的起始位置
              matrix.postTranslate(a.x - bitmap_line.getWidth() / 2 , a.y - bitmap_line.getHeight() /2);
              canvas.drawBitmap(bitmap_line, matrix, paint);
        }else{
             matrix.setScale(linelength / bitmap_line.getWidth(), 1);
                matrix.postTranslate(a.x - bitmap_line.getWidth() / 2 , a.y - bitmap_line.getHeight() /2);
             canvas.drawBitmap(bitmap_line_error, matrix, paint);
        }

2、得到线的旋转角度

// 获取角度
    public float getDegrees(Point pointA, Point pointB) {
 return (float) Math.toDegrees(Math.atan2(pointB.y - pointA.y, pointB.x - pointA.x));
    }

3、根据线的旋转角度以及线的长度,绘制出两点之间的连线。

/**画两点之间的连线*/
    private void line2Canvas(Point a,Point b,Canvas canvas) {
        // TODO Auto-generated method stub
        //得到线的长度
        float linelength=(float) Point.distance(a, b);
        float degress = getDegrees(a,b);
        canvas.rotate(degress, a.x, a.y);
        if(a.state==Point.STATE_PRESS){
            //设置线的缩放
              matrix.setScale(linelength / bitmap_line.getWidth(), 1);
              //设置线的起始位置
              matrix.postTranslate(a.x - bitmap_line.getWidth() / 2 , a.y - bitmap_line.getHeight() /2);
              canvas.drawBitmap(bitmap_line, matrix, paint);
        }else{
             matrix.setScale(linelength / bitmap_line.getWidth(), 1);
                matrix.postTranslate(a.x - bitmap_line.getWidth() / 2 , a.y - bitmap_line.getHeight() /2);
             canvas.drawBitmap(bitmap_line_error, matrix, paint);
        }
         canvas.rotate(-degress, a.x, a.y);
    }

4、在交叉点或移动线的时候也需要绘制连接线。

//画线
         if(pointList.size()>0){
             Point a=pointList.get(0);
             for(int i=1;i<pointList.size();i++){
                 Point b=pointList.get(i);
                 line2Canvas(a,b,canvas);
                 a=b;
             }
             if(isMoveNoPoint){//交叉点,连接线的时候
                 line2Canvas(a,new Point(moveX,moveY),canvas);
                }
         }

5、最终效果

Android13 解锁界面 android 设置解锁界面_九宫格_04


Android13 解锁界面 android 设置解锁界面_Android13 解锁界面_05

四、图案密码的设置与验证。

1、初始化密码参数。

//初始化密码参数
        int index = 1;
        for(int i=0;i<points.length;i++){
            for(int j=0;j<points[i].length;j++){
                Point point=points[i][j];
                point.index = index;
                index++;
            }
        }

2、绘制图案后,设置密码。

String passwordStr="";
                    for(int i=0;i<pointList.size();i++){
                    passwordStr+=pointList.get(i).index;
                }

3、设置密码回调监听

/**
     * 图案监听器
     */
    public static interface OnPatterChangeLister{
        void onPatterChange(String passwordStr);

        void onPatterStart(boolean isStart);
    }
    /**
     * 设置图案监听器
     * @param changeLister
     */
    public void SetOnPatterChangeLister(OnPatterChangeLister changeLister){
        if (changeLister != null) {
            this.onPatterChangeLister = changeLister;
        }
    }

4、对绘制正确、绘制错误时设置图案监听返回。

//绘制结束
        if(isFinis){
            if(pointList.size()==1){
                resetPoint();
            }else if(pointList.size()<POINT_SIZE&&pointList.size()>0){
                errorPoint();
                onPatterChangeLister.onPatterChange(null);
            }else{
                if(onPatterChangeLister!=null){
                    String passwordStr="";
                    for(int i=0;i<pointList.size();i++){
                    passwordStr+=pointList.get(i).index;
                }
                    onPatterChangeLister.onPatterChange(passwordStr);
                }
            }
        }

5、根据图案密码回调进行相应处理。

mLockPatterView.SetOnPatterChangeLister(new LockPatterView.OnPatterChangeLister() {
            @Override
            public void onPatterChange(String passwordStr) {
                if (passwordStr != null) {
                    if(pwdStr.equals("")){//设置密码
                        if(setPwd.equals("")){//第一次设置密码
                            setPwd=passwordStr;
                            pwdTv_.setText("请绘制确认密码");
                            mLockPatterView.resetPoint();
                        }else{
                            if(setPwd.equals(passwordStr)){
                                sharedPreferences.edit().putString("pwd",setPwd).commit();
                                Toast.makeText(MainActivity.this, "密码设置成功", Toast.LENGTH_LONG).show();
                                finish();
                            }else{
                                setPwd="";
                                pwdTv_.setText("两次密码绘制不一致,请重新绘制");
                                mLockPatterView.resetPoint();
                            }
                        }
                    }else{//验证密码
                        if(pwdStr.equals(passwordStr)){
                            Toast.makeText(MainActivity.this, "恭喜你,密码输入正确", Toast.LENGTH_LONG).show();
                            pwdStr="";
                            mLockPatterView.resetPoint();
                            pwdTv_.setText("请先绘制图案密码");
                        }else{
                            pwdTv_.setText("密码绘制错误,请重新绘制");
                            mLockPatterView.resetPoint();
                        }
                    }

                } else {
                    pwdTv_.setText("至少5个图案");
                }
            }
            @Override
            public void onPatterStart(boolean isStart) {

            }
        });

6、最终效果

Android13 解锁界面 android 设置解锁界面_android_06

五、源码下载