自定義圖案解鎖控件

看過極客學院自定義圖案解鎖控件教程,自己嘗試寫了一個。

效果圖:


1.首先要繪製九個點,先看兩張圖片

通過上面的兩張圖片可以很容易計算出沒個圓的圓心,也就是正方型的頂點座標。並保存這九個點,要保存這九個點必須先創建一個點類。

package com.example.chl.myapplication;

/**
 * Created by chl on 16-4-6.
 */
public class TuAnPoint {
    public static int STATE_NORMAL = 1;
    public static int STATE_PRESS = 2;
    public static int STATE_ERROR = 3;
    private float x;
    private float y;
    private int state = STATE_NORMAL;

    public TuAnPoint(float x, float y) {
        this.x = x;
        this.y = y;
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public float getX() {
        return x;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(float y) {
        this.y = y;
    }

    /**
     * 重寫equals方法來判斷兩個點對象是否相等
     * @param o
     * @return
     */
    @Override
    public boolean equals(Object o) {
        boolean bx = x == ((TuAnPoint) o).getX() ? true : false;
        boolean by = y == ((TuAnPoint) o).getY() ? true : false;
        boolean bb=false;
        if (bx && by){
            bb=true;
        }
        return bb;
    }
}
把這些座標轉化成我們定義的點對象並保存在List集合中:

float width = getWidth();
        float height = getHeight();
        float offsetX = 0;
        float offsetY = 0;
        float offset = 0;
        float length = 0;
        offset = Math.abs(width - height) / 2f;
        if (width > height) {
            //橫屏
            offsetX = offset;
            offsetY = 0;

            length = height / 4f;
        } else {
            //豎屏
            offsetX = 0;
            offsetY = offset;
            length = width / 4f;
        }
        listPoint.add(new TuAnPoint(offsetX + length, offsetY + length));
        listPoint.add(new TuAnPoint(offsetX + 2 * length, offsetY + length));
        listPoint.add(new TuAnPoint(offsetX + 3 * length, offsetY + length));
        listPoint.add(new TuAnPoint(offsetX + length, offsetY + 2 * length));
        listPoint.add(new TuAnPoint(offsetX + 2 * length, offsetY + 2 * length));
        listPoint.add(new TuAnPoint(offsetX + 3 * length, offsetY + 2 * length));
        listPoint.add(new TuAnPoint(offsetX + length, offsetY + 3 * length));
        listPoint.add(new TuAnPoint(offsetX + 2 * length, offsetY + 3 * length));
        listPoint.add(new TuAnPoint(offsetX + 3 * length, offsetY + 3 * length));
        isInit = true;

2.把保存的點繪製到屏幕上

1)把點在不同狀態下的資源圖片加載爲Bimap

private Bitmap normal = BitmapFactory.decodeResource(getResources(), R.drawable.normal);
    private Bitmap error = BitmapFactory.decodeResource(getResources(), R.drawable.error);
    private Bitmap press = BitmapFactory.decodeResource(getResources(), R.drawable.press);

2)繪製圖片

/**
     * 判斷點的狀態並繪製到屏幕上
     * @param canvas
     */
    private void drawPoint(Canvas canvas) {
        for (TuAnPoint tuAnPoint : listPoint) {
            if (tuAnPoint.getState() == TuAnPoint.STATE_PRESS) {
                canvas.drawBitmap(normal, tuAnPoint.getX() - radius, tuAnPoint.getY() - radius, null);
            } else if (tuAnPoint.getState() ==TuAnPoint.STATE_PRESS) {
                canvas.drawBitmap(press, tuAnPoint.getX() - radius, tuAnPoint.getY() - radius, null);
            } else {
                canvas.drawBitmap(error, tuAnPoint.getX() - radius, tuAnPoint.getY() - radius, null);
            }
        }
    }

因爲drawBitmap方法參數是圖片左上角的座標,所以要減去一個圖片的半徑。


3.onTouchEvent處理

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        mouseX = event.getX();
        mouseY = event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (pressList.size() > 0) {
                    for (TuAnPoint p : pressList) {
                        p.setState(TuAnPoint.STATE_NORMAL);
                    }
                    pressList.clear();
                }
                getSelectPoint();
                break;
            case MotionEvent.ACTION_MOVE:
                getSelectPoint();
                break;
            case MotionEvent.ACTION_UP:
                int pressCount = 0;
                for (TuAnPoint tuAnPoint : listPoint) {
                    if (!pressList.contains(tuAnPoint)) {
                        if (getPointsDistance(tuAnPoint.getX(), tuAnPoint.getY()) <= radius) {
                            pressCount++;
                        }
                    }
                }
                if (pressCount == 0 && pressList.size() > 0) {
                    mouseX = pressList.get(pressList.size() - 1).getX();
                    mouseY = pressList.get(pressList.size() - 1).getY();
                }
                boolean isFinish = false;
                if (finishedListener != null) {
                    isFinish = finishedListener.onDrawFinished(pressList);
                    if (!isFinish) {
                        for (TuAnPoint point : pressList) {
                            point.setState(TuAnPoint.STATE_ERROR);
                        }
                    }
                }
                break;

        }
        invalidate();
        return true;
    }

    /**
     * 判斷當前手指在屏幕上的點是否在九個點的範圍之類,如果是則選中改點
     */
    private void getSelectPoint() {
        for (TuAnPoint tuAnPoint : listPoint) {
            if (getPointsDistance(tuAnPoint.getX(), tuAnPoint.getY()) <= radius) {
                pressPoint = tuAnPoint;
                if (pressList.size() > 0) {
                    TuAnPoint p = pressList.get(pressList.size() - 1);


                    for (TuAnPoint isPress : listPoint) {
                        boolean b = isLine(p, isPress, pressPoint);
                        //Log.d("tuanjiesuo", "bb:" + b);
                        if (b) {
                            if (((isPress.getX() > p.getX() && isPress.getX() < pressPoint.getX()) && ((isPress.getY() > p.getY()) && (isPress.getY() < pressPoint.getY())))
                                    || ((isPress.getX() < p.getX() && isPress.getX() > pressPoint.getX()) && ((isPress.getY() < p.getY()) && (isPress.getY() > pressPoint.getY())))) {
                                //Log.d("tuanjiesuo","jinglai");
                                isPress.setState(TuAnPoint.STATE_PRESS);
                                if (!pressList.contains(isPress)) {

                                    pressList.add(isPress);
                                }
                            }
                            if ((isPress.getX() == p.getX() && isPress.getX() == pressPoint.getX() && isPress.getY() > p.getY() && isPress.getY() < pressPoint.getY())
                                    || (isPress.getX() == p.getX() && isPress.getX() == pressPoint.getX() && isPress.getY() < p.getY() && isPress.getY() > pressPoint.getY())) {
                                isPress.setState(TuAnPoint.STATE_PRESS);
                                if (!pressList.contains(isPress)) {

                                    pressList.add(isPress);
                                }
                            }
                            if ((isPress.getY() == p.getY() && isPress.getY() == pressPoint.getY() && isPress.getX() > p.getX() && isPress.getX() < pressPoint.getX())
                                    || (isPress.getY() == p.getY() && isPress.getY() == pressPoint.getY() && isPress.getX() < p.getX() && isPress.getX() > pressPoint.getX())) {
                                isPress.setState(TuAnPoint.STATE_PRESS);
                                if (!pressList.contains(isPress)) {

                                    pressList.add(isPress);
                                }

                            }
                            if ((isPress.getX() > p.getX() && isPress.getX() < pressPoint.getX() && isPress.getY() < p.getY() && isPress.getY() > pressPoint.getY())
                                    || (isPress.getX() < p.getX() && isPress.getX() > pressPoint.getX() && isPress.getY() > p.getY() && isPress.getY() < pressPoint.getY())) {
                                isPress.setState(TuAnPoint.STATE_PRESS);
                                if (!pressList.contains(isPress)) {

                                    pressList.add(isPress);
                                }
                            }
                            // break;
                        }
                    }
                }
                if (!pressList.contains(pressPoint)) {

                    pressList.add(pressPoint);
                }
                pressPoint.setState(TuAnPoint.STATE_PRESS);

            }
        }

    }

    /**
     * 判斷isPress這個點是否在點p點pressPoint的直線上
     *
     * @param p
     * @param isPress
     * @param pressPoint
     * @return
     */
    private boolean isLine(TuAnPoint p, TuAnPoint isPress, TuAnPoint pressPoint) {

        boolean b = (p.getY() - isPress.getY()) * (pressPoint.getX() - p.getX()) == (p.getX() - isPress.getX()) * (pressPoint.getY() - p.getY()) ? true : false;
        return b;
    }

    /**
     * 計算傳入的座標到手指在屏幕上點的距離
     * @param x
     * @param y
     * @return
     */
    private float getPointsDistance(float x, float y) {
        float distance = (float) Math.sqrt((x - mouseX) * (x - mouseX) + (y - mouseY) * (y - mouseY));
        return distance;
    }

    /**
     * 提供給外部接口,當繪製完成後調用
     */
    public interface OnDrawFinishedListener {
        boolean onDrawFinished(List<TuAnPoint> pressList);
    }

    /**
     * 提供外部傳入接口對象的方法
     * @param listener
     */
    public void setFinishedListener(OnDrawFinishedListener listener) {
        finishedListener = listener;
    }




最後是完整的項目代碼,註釋比較完整:

package com.example.chl.myapplication;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by chl on 16-4-6.
 */
public class TuAnView extends View {
    private List<TuAnPoint> listPoint = new ArrayList<TuAnPoint>();
    private List<TuAnPoint> pressList = new ArrayList<TuAnPoint>();//被選中的點保存的集合
    private boolean isInit = false;
    private Bitmap normal = BitmapFactory.decodeResource(getResources(), R.drawable.normal);
    private Bitmap error = BitmapFactory.decodeResource(getResources(), R.drawable.error);
    private Bitmap press = BitmapFactory.decodeResource(getResources(), R.drawable.press);
    private float radius = normal.getWidth() / 2f;//圖片半徑
    private float mouseX = 0;
    private float mouseY = 0;
    private TuAnPoint pressPoint;
    private Paint errorPaint;//當密碼錯誤時,畫線的畫筆
    private Paint pressPaint;//當密碼正確時,畫線的畫筆
    private OnDrawFinishedListener finishedListener;

    public TuAnView(Context context) {
        super(context);
        // init();
    }

    public TuAnView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // init();
    }

    public TuAnView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // init();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (!isInit) {
            init();
        }

        drawPoint(canvas);//畫點
        if (pressList.size() > 0) {//判斷當有點被選中時才畫線
            TuAnPoint a = pressList.get(0);//把集合中第一個點作爲線的開始點
            for (int i = 1; i < pressList.size(); i++) {
                drawLine(canvas, a, pressList.get(i));//畫線
                a = pressList.get(i);//把當前點作爲下次畫線的起點
            }
            drawLine(canvas, a, new TuAnPoint(mouseX, mouseY));//當有起點確定之後,手指在屏幕上滑動,但是沒確定終點時畫線
        }
    }

    /**
     * 畫線
     * @param canvas
     * @param a
     * @param b
     */
    private void drawLine(Canvas canvas, TuAnPoint a, TuAnPoint b) {
        if (a.getState() == TuAnPoint.STATE_PRESS) {
            canvas.drawLine(a.getX(), a.getY(), b.getX(), b.getY(), pressPaint);
        } else {
            canvas.drawLine(a.getX(), a.getY(), b.getX(), b.getY(), errorPaint);
        }

    }

    /**
     * 判斷點的狀態並繪製到屏幕上
     * @param canvas
     */
    private void drawPoint(Canvas canvas) {
        for (TuAnPoint tuAnPoint : listPoint) {
            if (tuAnPoint.getState() == TuAnPoint.STATE_PRESS) {
                canvas.drawBitmap(normal, tuAnPoint.getX() - radius, tuAnPoint.getY() - radius, null);
            } else if (tuAnPoint.getState() ==TuAnPoint.STATE_PRESS) {
                canvas.drawBitmap(press, tuAnPoint.getX() - radius, tuAnPoint.getY() - radius, null);
            } else {
                canvas.drawBitmap(error, tuAnPoint.getX() - radius, tuAnPoint.getY() - radius, null);
            }
        }
    }

    /**
     * 初始化
     */
    private void init() {
        errorPaint = new Paint();
        pressPaint = new Paint();
        errorPaint.setColor(Color.RED);
        errorPaint.setStrokeWidth(5);
        pressPaint.setColor(Color.YELLOW);
        pressPaint.setStrokeWidth(5);
        float width = getWidth();
        float height = getHeight();
        float offsetX = 0;
        float offsetY = 0;
        float offset = 0;
        float length = 0;
        offset = Math.abs(width - height) / 2f;
        if (width > height) {
            //橫屏
            offsetX = offset;
            offsetY = 0;

            length = height / 4f;
        } else {
            //豎屏
            offsetX = 0;
            offsetY = offset;
            length = width / 4f;
        }
        listPoint.add(new TuAnPoint(offsetX + length, offsetY + length));
        listPoint.add(new TuAnPoint(offsetX + 2 * length, offsetY + length));
        listPoint.add(new TuAnPoint(offsetX + 3 * length, offsetY + length));
        listPoint.add(new TuAnPoint(offsetX + length, offsetY + 2 * length));
        listPoint.add(new TuAnPoint(offsetX + 2 * length, offsetY + 2 * length));
        listPoint.add(new TuAnPoint(offsetX + 3 * length, offsetY + 2 * length));
        listPoint.add(new TuAnPoint(offsetX + length, offsetY + 3 * length));
        listPoint.add(new TuAnPoint(offsetX + 2 * length, offsetY + 3 * length));
        listPoint.add(new TuAnPoint(offsetX + 3 * length, offsetY + 3 * length));
        isInit = true;

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mouseX = event.getX();
        mouseY = event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (pressList.size() > 0) {
                    for (TuAnPoint p : pressList) {
                        p.setState(TuAnPoint.STATE_NORMAL);
                    }
                    pressList.clear();
                }
                getSelectPoint();
                break;
            case MotionEvent.ACTION_MOVE:
                getSelectPoint();
                break;
            case MotionEvent.ACTION_UP:
                int pressCount = 0;
                for (TuAnPoint tuAnPoint : listPoint) {
                    if (!pressList.contains(tuAnPoint)) {
                        if (getPointsDistance(tuAnPoint.getX(), tuAnPoint.getY()) <= radius) {
                            pressCount++;
                        }
                    }
                }
                if (pressCount == 0 && pressList.size() > 0) {
                    mouseX = pressList.get(pressList.size() - 1).getX();
                    mouseY = pressList.get(pressList.size() - 1).getY();
                }
                boolean isFinish = false;
                if (finishedListener != null) {
                    isFinish = finishedListener.onDrawFinished(pressList);
                    if (!isFinish) {
                        for (TuAnPoint point : pressList) {
                            point.setState(TuAnPoint.STATE_ERROR);
                        }
                    }
                }
                break;

        }
        invalidate();
        return true;
    }

    /**
     * 判斷當前手指在屏幕上的點是否在九個點的範圍之類,如果是則選中改點
     */
    private void getSelectPoint() {
        for (TuAnPoint tuAnPoint : listPoint) {
            if (getPointsDistance(tuAnPoint.getX(), tuAnPoint.getY()) <= radius) {
                pressPoint = tuAnPoint;
                if (pressList.size() > 0) {
                    TuAnPoint p = pressList.get(pressList.size() - 1);//得到此次繪製線時的起始點


                    for (TuAnPoint isPress : listPoint) {
                        /**
                         * 以爲有種情況是,當你選中第一個點後直接繞過第二個點而選中第三個點時,
                         * 默認第二個點也要選中,所以要做以下判斷
                         */
                        boolean b = isLine(p, isPress, pressPoint);//判斷是否還有其他點在起點和終點的直線上
                        //Log.d("tuanjiesuo", "bb:" + b);
                        if (b) {//如果有點在這條直線上,接下來判斷改點是在這兩點的中間還是兩端,是中間的話就要默認選中
                            if (((isPress.getX() > p.getX() && isPress.getX() < pressPoint.getX()) && ((isPress.getY() > p.getY()) && (isPress.getY() < pressPoint.getY())))
                                    || ((isPress.getX() < p.getX() && isPress.getX() > pressPoint.getX()) && ((isPress.getY() < p.getY()) && (isPress.getY() > pressPoint.getY())))) {
                                //Log.d("tuanjiesuo","jinglai");
                                isPress.setState(TuAnPoint.STATE_PRESS);
                                if (!pressList.contains(isPress)) {

                                    pressList.add(isPress);
                                }
                            }
                            if ((isPress.getX() == p.getX() && isPress.getX() == pressPoint.getX() && isPress.getY() > p.getY() && isPress.getY() < pressPoint.getY())
                                    || (isPress.getX() == p.getX() && isPress.getX() == pressPoint.getX() && isPress.getY() < p.getY() && isPress.getY() > pressPoint.getY())) {
                                isPress.setState(TuAnPoint.STATE_PRESS);
                                if (!pressList.contains(isPress)) {

                                    pressList.add(isPress);
                                }
                            }
                            if ((isPress.getY() == p.getY() && isPress.getY() == pressPoint.getY() && isPress.getX() > p.getX() && isPress.getX() < pressPoint.getX())
                                    || (isPress.getY() == p.getY() && isPress.getY() == pressPoint.getY() && isPress.getX() < p.getX() && isPress.getX() > pressPoint.getX())) {
                                isPress.setState(TuAnPoint.STATE_PRESS);
                                if (!pressList.contains(isPress)) {

                                    pressList.add(isPress);
                                }

                            }
                            if ((isPress.getX() > p.getX() && isPress.getX() < pressPoint.getX() && isPress.getY() < p.getY() && isPress.getY() > pressPoint.getY())
                                    || (isPress.getX() < p.getX() && isPress.getX() > pressPoint.getX() && isPress.getY() > p.getY() && isPress.getY() < pressPoint.getY())) {
                                isPress.setState(TuAnPoint.STATE_PRESS);
                                if (!pressList.contains(isPress)) {

                                    pressList.add(isPress);
                                }
                            }
                            // break;
                        }
                    }
                }
                if (!pressList.contains(pressPoint)) {

                    pressList.add(pressPoint);
                }
                pressPoint.setState(TuAnPoint.STATE_PRESS);

            }
        }

    }

    /**
     * 判斷isPress這個點是否在點p點pressPoint的直線上
     *
     * @param p
     * @param isPress
     * @param pressPoint
     * @return
     */
    private boolean isLine(TuAnPoint p, TuAnPoint isPress, TuAnPoint pressPoint) {

        boolean b = (p.getY() - isPress.getY()) * (pressPoint.getX() - p.getX()) == (p.getX() - isPress.getX()) * (pressPoint.getY() - p.getY()) ? true : false;
        return b;
    }

    /**
     * 計算傳入的座標到手指在屏幕上點的距離
     * @param x
     * @param y
     * @return
     */
    private float getPointsDistance(float x, float y) {
        float distance = (float) Math.sqrt((x - mouseX) * (x - mouseX) + (y - mouseY) * (y - mouseY));
        return distance;
    }

    /**
     * 提供給外部接口,當繪製完成後調用
     */
    public interface OnDrawFinishedListener {
        boolean onDrawFinished(List<TuAnPoint> pressList);
    }

    /**
     * 提供外部傳入接口對象的方法
     * @param listener
     */
    public void setFinishedListener(OnDrawFinishedListener listener) {
        finishedListener = listener;
    }
}

設置密碼的Activity:

package com.example.chl.myapplication;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Toast;

import java.util.List;

public class SetTuAnActivity extends AppCompatActivity {
    private  TuAnView tuAnView;
    private    List<TuAnPoint> pressList;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_set_tu_an);
        tuAnView=(TuAnView)findViewById(R.id.tuanjiesuo);
        tuAnView.setFinishedListener(new TuAnView.OnDrawFinishedListener() {
            @Override
            public boolean onDrawFinished(List<TuAnPoint> pressList) {
                SetTuAnActivity.this.pressList=pressList;
                return true;
            }
        });
    }
    public void click(View view){
        switch (view.getId()){
            case R.id.button1:
                ((MyAppliction)getApplication()).pressList=pressList;
                Toast.makeText(this,"保存成功",Toast.LENGTH_SHORT).show();
                finish();
                break;
            case R.id.button2:
                ((MyAppliction)getApplication()).pressList=null;
                break;
        }
    }

}

測試密碼的Activity:

public class TwoActivity extends AppCompatActivity {
    private  TuAnView tuAnView;
    private List<TuAnPoint> list;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_two);
        list=((MyAppliction)getApplication()).pressList;
        tuAnView=(TuAnView)findViewById(R.id.two_tuan);
        tuAnView.setFinishedListener(new TuAnView.OnDrawFinishedListener() {
            @Override
            public boolean onDrawFinished(List<TuAnPoint> pressList) {
                boolean b=true;
                if (pressList.size()!=list.size()){
                    b=false;
                }else {
                    for (int i = 0; i < pressList.size(); i++) {
                        if (!pressList.get(i).equals(list.get(i))) {
                            b = false;
                        }

                    }
                }
                if (b){
                    Toast.makeText(TwoActivity.this,"密碼正確",Toast.LENGTH_SHORT).show();
                }else {
                    Toast.makeText(TwoActivity.this,"密碼錯誤",Toast.LENGTH_SHORT).show();
                }
                return b;
            }
        });
    }

}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章