android图案解锁功能的实现

我们经常会在app中看到图案解锁的功能,所以寻思做一个,在某客视频上看到了教程,自己跟着做了一遍,记录一下,顺便理清一下思路。

思路讲解:

首先自定义一个图案的view,其中实现onDraw方法,以及添加接口回调进行验证密码正确性。
自定义view效果如下:
android图案解锁功能的实现_第1张图片

首先新建一个记录点的坐标的一个bean

public class Point {
    //表示图案状态
    public static  int STATE_NORMAL=0;
    public static  int STATE_PRESS=1;
    public static  int STATE_ERROR=2;
    //坐标
    float x,y;
    int state=STATE_NORMAL;

    public Point(float x, float y) {
        this.x = x;
        this.y = y;
    }
    //判断按下屏幕的点是否在某一个圆圈的区域内
    public  float distance(Point a)
    {
        float distance= (float) Math.sqrt((x-a.x)*(x-a.x)+(y-a.y)*(y-a.y));
        return  distance;
    }

}

下面是自定义view代码:

public class GestureLockView extends View {
    //三张不同状态圆的图片
    private Bitmap error,normal,press;
    //
    private ArrayList pointlist=new ArrayList<>();

    //设置圆的坐标位置的数组
    private  Point[][] points=new Point[3][3];

    //画圆的画笔(后面的参数为抗锯齿的作用)
    private Paint  paint=new Paint(Paint.ANTI_ALIAS_FLAG);
    //设置回调监听事件
    private OnDrawFinsihListner listner;

    //保存圆的半径
    private float radius;
    public GestureLockView(Context context) {
        super(context);
    }

    public GestureLockView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public GestureLockView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    //判断是否初始化
    private boolean inited=false;
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //ondraw方法会多次调用所以要设置标记位判断是否调用过
        if (!inited){
            init();
        }
        //画图
        drawPoints(canvas);
        //画线
        if (pointlist.size()>0)
        {   //冲重第一个点画起
            Point a=pointlist.get(0);
            for (int i=1;iif (isDraw)
            {
                drawLine(canvas,a,new Point(mouseX,mouseY));
            }
        }
    }

    //绘制圆圈背景图案
    private  void drawPoints(Canvas canvas){
        for (int i=0;ifor (int j=0;jif (points[i][j].state==Point.STATE_NORMAL)
                {
                    //正常状态
                    canvas.drawBitmap(normal,points[i][j].x-radius,points[i][j].y-radius,paint);
                }
                else  if (points[i][j].state==Point.STATE_PRESS)
                {
                    //按下状态
                    canvas.drawBitmap(press,points[i][j].x-radius,points[i][j].y-radius,paint);
                }else
                {
                    //错误状态
                    canvas.drawBitmap(error,points[i][j].x-radius,points[i][j].y-radius,paint);
                }
            }
        }
    }

    //初始化
    private  void init(){
        pressPaint.setColor(Color.BLUE);
        //画笔宽度
        pressPaint.setStrokeWidth(5);

        errorPaint.setColor(Color.RED);
        errorPaint.setStrokeWidth(5);


        //获取图片
        error= BitmapFactory.decodeResource(getResources(),R.drawable.error);
        press=BitmapFactory.decodeResource(getResources(),R.drawable.press);
        normal=BitmapFactory.decodeResource(getResources(),R.drawable.normal);

        radius=error.getHeight()/2;

        int width=getWidth();
        Log.e("width",width+"");
        int height=getHeight();
        Log.e("height",height+"");

        //显示位置的偏移量
        int offset=Math.abs(width-height)/2;

        //图案偏移量
        int offsetX,offsetY;

        //小方格偏移量
        int space;

        //判断横屏还是竖屏
        if (width>height){
            offsetX=offset;
            offsetY=0;
            space=height/4;
        }
        else {
            space=width/4;
            offsetX=0;
            offsetY=offset;

        }
        //绘制圆圈
        for (int i=0;i<3;i++)
        {
            for (int j=0;j<3;j++){
                points[i][j]=new Point(offsetX+space*(j+1),offsetY+space*(i+1));
            }

        }
        inited=true;


    }
    //获取触摸的点
    private  float mouseX,mouseY;

    //是否在绘制状态
    private  boolean isDraw=false;

    //触摸事件
    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        //得到按下坐标
        mouseX=event.getX();
        mouseY=event.getY();
        int[] ij;
        int i,j;
        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                //清空绘制过程
                resetPoint();
                ij=getSelectionPoint();
                if (ij!=null)
                {
                    isDraw=true;
                     i=ij[0];
                     j=ij[1];
                    points[i][j].state=Point.STATE_PRESS;
                    pointlist.add(points[i][j]);
                    passlist.add(i*3+j);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (isDraw)
                {
                     ij=getSelectionPoint();
                    if (ij!=null)
                    {
                         i=ij[0];
                         j=ij[1];
                        if (!pointlist.contains(points[i][j]))
                        {
                            points[i][j].state=Point.STATE_PRESS;
                            pointlist.add(points[i][j]);
                            passlist.add(i*3+j);
                        }


                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                boolean valid=false;
                if (listner!=null&&isDraw)
                {
                    valid=listner.OnDrawFinished(passlist);
                }
                if (!valid)
                {
                    for (Point point:pointlist)
                        point.state=Point.STATE_ERROR;
                }
                isDraw=false;
                break;
        }
        this.postInvalidate();
        return true;
    }
    //当手指按下,判断属于那个圆圈
    private int[] getSelectionPoint()
    {

        Point pMouse=new Point(mouseX,mouseY);
        for (int i=0;ifor (int j=0;jif (points[i][j].distance(pMouse)int[] result=new int[2];
                    result[0]=i;
                    result[1]=j;
                    return result;

                }

            }
        }
        return  null;
    }
    private Paint errorPaint=new Paint();
    private Paint pressPaint=new Paint();
    private  void drawLine(Canvas canvas,Point a,Point b)
    {
        if (a.state==Point.STATE_PRESS)
        {
            canvas.drawLine(a.x,a.y,b.x,b.y,pressPaint);
        }
        else if (a.state==Point.STATE_ERROR){
            canvas.drawLine(a.x,a.y,b.x,b.y,errorPaint);
        }
    }

    public  void  resetPoint(){
        pointlist.clear();
        passlist.clear();

        for (int i=0;ifor (int k=0;kthis.postInvalidate();
    }

    private  ArrayList passlist=new ArrayList<>();

    public interface OnDrawFinsihListner
    {
        boolean OnDrawFinished(List passlist);
    }

    public  void setOnDrawFinishedListener(OnDrawFinsihListner listener)
    {
        this.listner=listener;
    }
}

主页面布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="设置图案密码"
        android:id="@+id/button"
        android:layout_gravity="center_horizontal" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="测试图案密码"
        android:id="@+id/button2"
        android:layout_gravity="center_horizontal" />
LinearLayout>

主页面代码:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SettingAct.class);
                startActivity(intent);
            }
        });
        findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(MainActivity.this,LockAct.class);
                startActivity(intent);
            }
        });
    }

}

设置页面代码:

public class SettingAct extends AppCompatActivity  {
    private GestureLockView lockView;
    private List passlist;
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_setting);
        lockView= (GestureLockView) findViewById(R.id.view);
        findViewById(R.id.btn_reset).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                lockView.resetPoint();
            }
        });
        findViewById(R.id.btn_save).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (passlist!=null)
                {
                    StringBuilder sb=new StringBuilder();
                    for (Integer i:passlist)
                    {
                        sb.append(i);
                    }
                    SharedPreferences sharedPreferences=SettingAct.this.getSharedPreferences("password", Context.MODE_PRIVATE);
                    SharedPreferences.Editor editor=sharedPreferences.edit();
                    editor.putString("password", sb.toString());
                    editor.commit();
                    Toast.makeText(SettingAct.this,"保存完成",Toast.LENGTH_SHORT).show();
                    Intent intent=new Intent(SettingAct.this,MainActivity.class);
                    startActivity(intent);


                }

            }
        });
        lockView.setOnDrawFinishedListener(new GestureLockView.OnDrawFinsihListner() {
            @Override
            public boolean OnDrawFinished(List passlist) {
                if (passlist.size()<3)
                {
                    Toast.makeText(SettingAct.this,"密码不能小于三个点",Toast.LENGTH_SHORT).show();
                    return  false;
                }else
                {
                    SettingAct.this.passlist=passlist;
                    return  true;

                }

            }
        });
    }


}

测试页面代码:

public class LockAct extends AppCompatActivity {
private   String passwaord;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lock);
        GestureLockView gestureLockView= (GestureLockView) findViewById(R.id.view2);
        SharedPreferences sharedPreferences=getSharedPreferences("password", Context.MODE_PRIVATE);
       passwaord =sharedPreferences.getString("password","");
        Log.e("password",passwaord);
        gestureLockView.setOnDrawFinishedListener(new GestureLockView.OnDrawFinsihListner() {
            @Override
            public boolean OnDrawFinished(List passlist) {
                StringBuilder stringBuilder=new StringBuilder();
                for (Integer i:passlist)
                {
                    stringBuilder.append(i);
                }
                Log.e("sb",stringBuilder.toString());

                if (stringBuilder.toString().equals(passwaord))
                {
                    Toast.makeText(LockAct.this, "密码正确", Toast.LENGTH_SHORT).show();
                    return true;
                }
                else
                {
                    Toast.makeText(LockAct.this, "密码错误", Toast.LENGTH_SHORT).show();
                    return  false;
                }
            }
        });
    }

}

最终效果图:
android图案解锁功能的实现_第2张图片

注意:
这里很奇怪,为什么布局显示正常的图案,但是在模拟器中圆圈就变大了,然后我发现换为API 15的页面布局中显示出来的就是当前的结果,如果哪位大大知道为什么请告诉我好么!!!!!!!

你可能感兴趣的:(android之自定义view)