我們經常會在app中看到圖案解鎖的功能,所以尋思做一個,在某客視頻上看到了教程,自己跟着做了一遍,記錄一下,順便理清一下思路。
思路講解:
首先自定義一個圖案的view,其中實現onDraw方法,以及添加接口回調進行驗證密碼正確性。
自定義view效果如下:
首先新建一個記錄點的座標的一個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<Point> 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;i<pointlist.size();i++)
{
Point b=pointlist.get(i);
drawLine(canvas,a,b);
a=b;
}
if (isDraw)
{
drawLine(canvas,a,new Point(mouseX,mouseY));
}
}
}
//繪製圓圈背景圖案
private void drawPoints(Canvas canvas){
for (int i=0;i<points.length;i++ )
{
for (int j=0;j<points[i].length;j++)
{
if (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;i<points.length;i++){
for (int j=0;j<points[i].length;j++)
{
if (points[i][j].distance(pMouse)<radius)
{
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;i<points.length;i++)
{
for (int k=0;k<points[i].length;k++)
points[i][k].state=Point.STATE_NORMAL;
}
this.postInvalidate();
}
private ArrayList<Integer> passlist=new ArrayList<>();
public interface OnDrawFinsihListner
{
boolean OnDrawFinished(List<Integer> 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<Integer> 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<Integer> 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<Integer> 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;
}
}
});
}
}
最終效果圖: