自定义图案解锁控件

看过极客学院自定义图案解锁控件教程,自己尝试写了一个。

效果图:


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;
            }
        });
    }

}



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