自定义九宫格解锁布局

自定义九宫格解锁是一个特别常见的布局,在一些金融类App里面见到的比较多,下面,我们就通过代码来实现一下这个布局.
第一步,我们先创建一个LockPatternView继承自View.

/**
 * 自定义九宫格
 */
public class LockPatternView extends View {
    public LockPatternView(Context context) {
        this(context,null);
    }

    public LockPatternView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public LockPatternView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

第二步,绘制九个格子,每排三个,每个格子占屏幕宽的1/3(注意,竖屏就是占高的1/3)
先我们定义一个内部类Point.

class Point{
        //默认状态
        public static final int STATUS_NORMAL = 1;
        //选择状态
        public static final int STATUS_PRESSED = 2;
        //错误状态
        public static final int STATUS_ERROR = 3;
        //通过状态
        public static final int STATUS_PASS = 4;
        //中心点x
        int centerX;
        //中心点y
        int centerY;
        //下标,就是我们定义的密码1-9
        int index;
        //当前点的状态
        int status = STATUS_NORMAL;

        public Point(int centerX, int centerY, int index) {
            this.centerX = centerX;
            this.centerY = centerY;
            this.index = index;
        }

接下来我们定义一个3*3的数组来保存这9个点

private Point[][] mPoints = new Point[3][3];

下一步我们来获取这9个点的中心点的坐标

// 外圆的半径
private int mDotRadius = 0;
//内圆半径
private int mInnerDotRadius = 0;

private void  initDot(){
        int mWidth = getWidth() - getPaddingLeft() - getPaddingRight();
        int mHeight = getHeight() - getPaddingTop() - getPaddingBottom();


        int offsetX = 0;
        int offsetY = 0;

        //这里就是我们支持横屏或者竖屏的代码
        if (mWidth > mHeight) {
            offsetX = (mWidth - mHeight) / 2 + getPaddingLeft();
            offsetY = getPaddingTop();
            mWidth = mHeight;
        } else {
            offsetX = getPaddingLeft();
            offsetY = (mHeight - mWidth) / 2 + getPaddingTop();
            mHeight = mWidth;
        }
        //每个点占1/3
        int squareWidth = mWidth/3;
        //外圆的半径为点的宽度的1/3,这样不会显得挤
        mDotRadius = squareWidth/3;
        //内圆半径为外圆半径的1/6
        mInnerDotRadius = mDotRadius/6;

        for (int i = 0;i<mPoints.length;i++) {
            for (int j = 0;j<mPoints[i].length;j++) {
                // 循环获取九个点
                mPoints[i][j] = new Point(offsetX + squareWidth * (j * 2 + 1) / 2,
                        offsetY + squareWidth * (i * 2 + 1) / 2, i * mPoints.length + j+1);
            }
        }
    }

这时候我们已经完成了9个点的中心点坐标获取,下一步我们来绘制这九个点.
这个点我们只需要初始化一次,所以我们先定义一个变量来保存初始化状态

private boolean mIsInit = false;

下一步,我们需要初始化我们的画笔

// 默认的外圆画笔
    private Paint mOuterNormalPaint;
    // 默认的内圆画笔
    private Paint mInnerNormalPaint;
    /**
     * 初始化画笔
     *
     */
    private void initPaint() {
        // 默认的外圆画笔
        mOuterNormalPaint = new Paint();
        //颜色可以自定义
        mOuterNormalPaint.setColor(Color.BLACK);
        //外圆为空心的
        mOuterNormalPaint.setStyle(Paint.Style.STROKE);
        mOuterNormalPaint.setAntiAlias(true);
        mOuterNormalPaint.setStrokeWidth(mDotRadius / 9);


        // 默认的内圆画笔
        mInnerNormalPaint = new Paint();
        //颜色可以自定义
        mInnerNormalPaint.setColor(Color.BLACK);
        //内圆为实心的
        mInnerNormalPaint.setStyle(Paint.Style.FILL);
        mInnerNormalPaint.setAntiAlias(true);
        mInnerNormalPaint.setStrokeWidth(mDotRadius / 6);
    }

接下来我们在onDraw方法里面去初始化我们的点

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (!mIsInit){
            initDot();
            initPaint();
            mIsInit = true;
        }
    }

下一步,我们来绘制这9个点

private void drawDot(Canvas canvas) {
        for (int i = 0;i<mPoints.length;i++) {
            for (int j = 0;j<mPoints[i].length;j++) {
                // 循环绘制九个点
                Point point = mPoints[i][j];
                if (Point.STATUS_NORMAL == point.status) {
                    canvas.drawCircle(point.centerX, point.centerY, mInnerDotRadius, mInnerNormalPaint);
                    canvas.drawCircle(point.centerX, point.centerY, mDotRadius, mOuterNormalPaint);
                }
            }
        }
    }

然后,我们在onDraw调用drawDot(canvas)方法.
我们来运行一遍
在这里插入图片描述
现在我们的运行效果是这样的,你会发现触摸是没用的,下一步我们开始处理触摸事件.

首先我们重写onTouchEvent方法
学过事件分发的人应该知道,如果ACTION_DOWN事件不返回true,后面的事件我们是收不到的,所以我们的onTouchEvent方法我们直接返回true.
我们先来处理一下ACTION_DOWN事件.我们先来分析一下.
我们先获取一下按下的点的坐标,然后我们去查找一下这个点有没有在这9个点内部,如果在,就将这个点的status设置为STATUS_PRESSED.
我们看下代码.

@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Point pressPoint = getTouchPoint(event.getX(),event.getY());
                if (null != pressPoint){
                    pressPoint.status = Point.STATUS_PRESSED;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        invalidate();
        return true;
    }
    
    //获取当前触摸位置的点
    public Point getTouchPoint(float x,float y){
        for (int i = 0;i<mPoints.length;i++) {
            for (int j = 0;j<mPoints[i].length;j++) {
                // 循环遍历九个点
                Point point = mPoints[i][j];

                if (x>point.centerX-mDotRadius && x < point.centerX+mDotRadius && y>point.centerY-mDotRadius && y<point.centerY+mDotRadius){
                    return point;
                }
            }
        }
        return null;
    }

下一步我们需要在绘制点的方法里面去加一个判断,绘制按下的点.
先第一步初始化按下的点绘制的画笔.

// 按下的外圆画笔
    private Paint mOuterPressedPaint;
    // 按下的内圆画笔
    private Paint mInnerPressedPaint;

	// 按下的外圆画笔
        mOuterPressedPaint = new Paint();
        mOuterPressedPaint.setColor(Color.BLUE);
        mOuterPressedPaint.setStyle(Paint.Style.STROKE);
        mOuterPressedPaint.setAntiAlias(true);
        mOuterPressedPaint.setStrokeWidth(mDotRadius / 9);

        // 按下的内圆画笔
        mInnerPressedPaint = new Paint();
        mInnerPressedPaint.setColor(Color.BLUE);
        mInnerPressedPaint.setStyle(Paint.Style.FILL);
        mInnerPressedPaint.setAntiAlias(true);
        mInnerPressedPaint.setStrokeWidth(mDotRadius / 6);

Paint的初始化一样放到initPaint方法里面

接着我们在drawDot方法里面增加status==STATUS_PRESSED的判断

else if(Point.STATUS_PRESSED == point.status){
                    canvas.drawCircle(point.centerX, point.centerY, mInnerDotRadius, mInnerPressedPaint);
                    canvas.drawCircle(point.centerX, point.centerY, mDotRadius, mOuterPressedPaint);
                }

这时候我们再运行一下,效果如下
在这里插入图片描述
我们可以看到,现在我们按下时有效果的,但是滑动还是没反应,下一步我们处理滑动事件.
还在跟之前的思想一样,我们拿到划过的坐标,然后去查找这个位置是不是在点内部,是的话就将点的status设置为STATUS_PRESSED ,代码如下;

case MotionEvent.ACTION_MOVE:
                Point movePoint = getTouchPoint(event.getX(),event.getY());
                if (null != movePoint){
                    movePoint.status = Point.STATUS_PRESSED;
                }

效果如下
在这里插入图片描述
我们可以看到,这时侯我们已经滑动有效果了.
下一步我们绘制一下连线.
首先我们定义一个List来保存我们触摸过的点,

private List<Point> mSelectPoints = new ArrayList<Point>();

然后在onTouch事件中把按下的点和触摸过的点保存起来

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Point pressPoint = getTouchPoint(event.getX(),event.getY());
                if (null != pressPoint){
                    mSelectPoints.add(pressPoint);
                    pressPoint.status = Point.STATUS_PRESSED;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                Point movePoint = getTouchPoint(event.getX(),event.getY());
                if (null != movePoint){
                    if (!mSelectPoints.contains(movePoint)) {
                        mSelectPoints.add(movePoint);
                    }
                    movePoint.status = Point.STATUS_PRESSED;
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        invalidate();
        return true;
    }

还有个问题,我们一个点但没有到下一个点的时候我们也需要画最后一个点到当前位置的线,所以我们需要将当前的位置保存起来,在这里我们直接用全局变量保存就好了.

case MotionEvent.ACTION_MOVE:
                moveX = event.getX();
                moveY = event.getY();

直接赋值就好,接下来我们来绘制连线.
因为前面我们已经将当前触摸过的点保存起来了,所以我们只需要绘制点与点之间的连线和最后一个点到当前位置的连线.


// 初始化线的画笔
        mLinePaint = new Paint();
        mLinePaint.setColor(Color.BLUE);
        mLinePaint.setStyle(Paint.Style.STROKE);
        mLinePaint.setAntiAlias(true);
        mLinePaint.setStrokeWidth(mDotRadius / 9);
//在onDraw方法里面调用drawLine
@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (!mIsInit){
            initDot();
            initPaint();
            mIsInit = true;
        }
        drawDot(canvas);
        if (mSelectPoints.size()>0){
            drawLine(canvas);
        }
    }
    
private void drawLine(Canvas canvas){
        //绘制最后一点到当前位置的线
        if (moveX != 0 && moveY != 0) {
            Point lastPoint = mSelectPoints.get(mSelectPoints.size() - 1);
            canvas.drawLine(lastPoint.centerX, lastPoint.centerY, moveX, moveY, mLinePaint);
        }
        if (mSelectPoints.size()>1) {
            for (int i = 0; i < mSelectPoints.size()-1; i++) {
                Point point = mSelectPoints.get(i);
                Point nextPoint = mSelectPoints.get(i+1);
                if(Point.STATUS_PRESSED == point.status){
                    canvas.drawLine(point.centerX, point.centerY, nextPoint.centerX, nextPoint.centerY, mLinePaint);
                }
            }
        }
    }

我们可以看下效果.
在这里插入图片描述
这个效果还是ok的,但我们发现有个问题,就是我们按下时如果没有按中点,按道理后面 的触摸也应该是没有效果的,所以我们需要先判断一下按下时是不是按中了点,再去处理MOVE事件,我们定义一个变量
private boolean mIsTouchPoint = false;
接下来在ACTION_DOWN事件中当按下的点是有效的,我们将再去处理ACTION_MOVE事件,代码如下.

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mIsTouchPoint = false;
                Point pressPoint = getTouchPoint(event.getX(),event.getY());
                if (null != pressPoint){
                    mSelectPoints.add(pressPoint);
                    pressPoint.status = Point.STATUS_PRESSED;
                    mIsTouchPoint = true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (mIsTouchPoint) {
                    moveX = event.getX();
                    moveY = event.getY();
                    Point movePoint = getTouchPoint(moveX, moveY);
                    if (null != movePoint) {
                        if (!mSelectPoints.contains(movePoint)) {
                            mSelectPoints.add(movePoint);
                        }
                        movePoint.status = Point.STATUS_PRESSED;
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        invalidate();
        return true;
    }

这时候大家再跑一遍,会发现上面的问题解决了.

接下来我们来处理ACTION_UP事件.
在开始前我们需要提前获取到密码保存起来,以备touch完毕后检验.
接下来就是判断touch的值和设置进来的值是否相等来绘制成功和失败的效果,并回调给调用者.代码先不贴,最后来贴一个完整的.
先来看看效果
在这里插入图片描述
这个是错误的效果
在这里插入图片描述
这个是成功的.

下面我把代码全部贴出来给大家看看,需要的可以拷贝到自己的项目中去,谢谢大家.

package cn.npe1348.lib_lockpatternview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

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

/**
 * 自定义九宫格
 * 1.绘制九个格子,每排三个,每个格子占屏幕宽的1/3(注意,竖屏就是占高的1/3)
 */
public class LockPatternView extends View {

    private String mPassword = "123456";
    private Point[][] mPoints = new Point[3][3];
    // 外圆的半径
    private int mDotRadius = 0;
    //内圆半径
    private int mInnerDotRadius = 0;

    private boolean mIsInit = false;

    private boolean mIsTouchPoint = false;

    // 默认的外圆画笔
    private Paint mOuterNormalPaint;
    // 默认的内圆画笔
    private Paint mInnerNormalPaint;

    // 按下的外圆画笔
    private Paint mOuterPressedPaint;
    // 按下的内圆画笔
    private Paint mInnerPressedPaint;
    //默认线画笔
    private Paint mLinePaint;

    private Paint mErrorLinePaint;
    private Paint mPassLinePaint;
    private Paint mOuterErrorPaint;
    private Paint mOuterPassPaint;
    private Paint mInnerErrorPaint;
    private Paint mInnerPassPaint;

    private List<Point> mSelectPoints = new ArrayList<Point>();

    private float moveX;
    private float moveY;

    private OnUnLockListener mOnUnLockListener;

    public void setUnLockListener(OnUnLockListener onUnLockListener){
        this.mOnUnLockListener = onUnLockListener;
    }

    public interface OnUnLockListener{
        public void isUnLockSuccess(boolean success,String selectIndexStr);
    }

    public LockPatternView(Context context) {
        this(context,null);
    }

    public LockPatternView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public LockPatternView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setPassword(String password) {
        this.mPassword = mPassword;
    }

    private void initDot(){
        int mWidth = getWidth() - getPaddingLeft() - getPaddingRight();
        int mHeight = getHeight() - getPaddingTop() - getPaddingBottom();


        int offsetX = 0;
        int offsetY = 0;

        //这里就是我们支持横屏或者竖屏的代码
        if (mWidth > mHeight) {
            offsetX = (mWidth - mHeight) / 2 + getPaddingLeft();
            offsetY = getPaddingTop();
            mWidth = mHeight;
        } else {
            offsetX = getPaddingLeft();
            offsetY = (mHeight - mWidth) / 2 + getPaddingTop();
            mHeight = mWidth;
        }
        //每个点占1/3
        int squareWidth = mWidth/3;
        //外圆的半径为点的宽度的1/3,这样不会显得挤
        mDotRadius = squareWidth/3;
        //内圆半径为外圆半径的1/6
        mInnerDotRadius = mDotRadius/6;

        for (int i = 0;i<mPoints.length;i++) {
            for (int j = 0;j<mPoints[i].length;j++) {
                // 循环获取九个点
                mPoints[i][j] = new Point(offsetX + squareWidth * (j * 2 + 1) / 2,
                        offsetY + squareWidth * (i * 2 + 1) / 2, i * mPoints.length + j+1);
            }
        }
    }

    /**
     * 初始化画笔
     *
     */
    private void initPaint() {
        // 默认的外圆画笔
        mOuterNormalPaint = new Paint();
        //颜色可以自定义
        mOuterNormalPaint.setColor(Color.BLACK);
        //外圆为空心的
        mOuterNormalPaint.setStyle(Paint.Style.STROKE);
        mOuterNormalPaint.setAntiAlias(true);
        mOuterNormalPaint.setStrokeWidth(mDotRadius / 9);


        // 默认的内圆画笔
        mInnerNormalPaint = new Paint();
        //颜色可以自定义
        mInnerNormalPaint.setColor(Color.BLACK);
        //内圆为实心的
        mInnerNormalPaint.setStyle(Paint.Style.FILL);
        mInnerNormalPaint.setAntiAlias(true);
        mInnerNormalPaint.setStrokeWidth(mDotRadius / 6);

        // 按下的外圆画笔
        mOuterPressedPaint = new Paint();
        mOuterPressedPaint.setColor(Color.BLUE);
        mOuterPressedPaint.setStyle(Paint.Style.STROKE);
        mOuterPressedPaint.setAntiAlias(true);
        mOuterPressedPaint.setStrokeWidth(mDotRadius / 9);

        // 按下的内圆画笔
        mInnerPressedPaint = new Paint();
        mInnerPressedPaint.setColor(Color.BLUE);
        mInnerPressedPaint.setStyle(Paint.Style.FILL);
        mInnerPressedPaint.setAntiAlias(true);
        mInnerPressedPaint.setStrokeWidth(mDotRadius / 6);

        // 线的画笔
        mLinePaint = new Paint();
        mLinePaint.setColor(Color.BLUE);
        mLinePaint.setStyle(Paint.Style.STROKE);
        mLinePaint.setAntiAlias(true);
        mLinePaint.setStrokeWidth(mDotRadius / 9);

        // 错误的线的画笔
        mErrorLinePaint = new Paint();
        mErrorLinePaint.setColor(Color.RED);
        mErrorLinePaint.setStyle(Paint.Style.STROKE);
        mErrorLinePaint.setAntiAlias(true);
        mErrorLinePaint.setStrokeWidth(mDotRadius / 9);
        // 通过的线的画笔
        mPassLinePaint = new Paint();
        mPassLinePaint.setColor(Color.GREEN);
        mPassLinePaint.setStyle(Paint.Style.STROKE);
        mPassLinePaint.setAntiAlias(true);
        mPassLinePaint.setStrokeWidth(mDotRadius / 9);

        //错误的外圆画笔
        mOuterErrorPaint = new Paint();
        mOuterErrorPaint.setColor(Color.RED);
        mOuterErrorPaint.setStyle(Paint.Style.STROKE);
        mOuterErrorPaint.setAntiAlias(true);
        mOuterErrorPaint.setStrokeWidth(mDotRadius / 9);
        //通过的外圆画笔
        mOuterPassPaint = new Paint();
        mOuterPassPaint.setColor(Color.GREEN);
        mOuterPassPaint.setStyle(Paint.Style.STROKE);
        mOuterPassPaint.setAntiAlias(true);
        mOuterPassPaint.setStrokeWidth(mDotRadius / 9);

        //错误的内圆画笔
        mInnerErrorPaint = new Paint();
        mInnerErrorPaint.setColor(Color.RED);
        mInnerErrorPaint.setStyle(Paint.Style.FILL);
        mInnerErrorPaint.setAntiAlias(true);
        mInnerErrorPaint.setStrokeWidth(mDotRadius / 6);
        //通过的内圆画笔
        mInnerPassPaint = new Paint();
        mInnerPassPaint.setColor(Color.GREEN);
        mInnerPassPaint.setStyle(Paint.Style.FILL);
        mInnerPassPaint.setAntiAlias(true);
        mInnerPassPaint.setStrokeWidth(mDotRadius / 6);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (!mIsInit){
            initDot();
            initPaint();
            mIsInit = true;
        }
        drawDot(canvas);
        if (mSelectPoints.size()>0){
            drawLine(canvas);
        }
    }

    private void drawLine(Canvas canvas){
        //绘制最后一点到当前位置的线
        if (moveX != 0 && moveY != 0) {
            Point lastPoint = mSelectPoints.get(mSelectPoints.size() - 1);
            canvas.drawLine(lastPoint.centerX, lastPoint.centerY, moveX, moveY, mLinePaint);
        }
        if (mSelectPoints.size()>1) {
            for (int i = 0; i < mSelectPoints.size()-1; i++) {
                Point point = mSelectPoints.get(i);
                Point nextPoint = mSelectPoints.get(i+1);
                if(Point.STATUS_PRESSED == point.status){
                    canvas.drawLine(point.centerX, point.centerY, nextPoint.centerX, nextPoint.centerY, mLinePaint);
                }else if(Point.STATUS_ERROR == point.status){
                    canvas.drawLine(point.centerX, point.centerY, nextPoint.centerX, nextPoint.centerY, mErrorLinePaint);
                }else if(Point.STATUS_PASS == point.status){
                    canvas.drawLine(point.centerX, point.centerY, nextPoint.centerX, nextPoint.centerY, mPassLinePaint);
                }
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mIsTouchPoint = false;
                Point pressPoint = getTouchPoint(event.getX(),event.getY());
                if (null != pressPoint){
                    mSelectPoints.add(pressPoint);
                    pressPoint.status = Point.STATUS_PRESSED;
                    mIsTouchPoint = true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (mIsTouchPoint) {
                    moveX = event.getX();
                    moveY = event.getY();
                    Point movePoint = getTouchPoint(moveX, moveY);
                    if (null != movePoint) {
                        if (!mSelectPoints.contains(movePoint)) {
                            mSelectPoints.add(movePoint);
                        }
                        movePoint.status = Point.STATUS_PRESSED;
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                if (mIsTouchPoint) {
                    moveX = 0;
                    moveY = 0;
                    String selectIndexStr = getSelectPointIndexStr();
                    if (null != mPassword && mPassword.equals(selectIndexStr)) {
                        if (null != mOnUnLockListener) {
                            mOnUnLockListener.isUnLockSuccess(true,selectIndexStr);
                        }
                        setAllSelectPointPass();
                    }else{
                        if (null != mOnUnLockListener) {
                            mOnUnLockListener.isUnLockSuccess(false,selectIndexStr);
                        }
                        setAllSelectPointError();
                    }
                }
                break;
        }
        invalidate();
        return true;
    }
    /**
     * 获取选中的下标字符串
     * @return
     */
    private String getSelectPointIndexStr(){
        String indexStr = "";
        for (int i = 0; i < mSelectPoints.size(); i++){
            indexStr+=mSelectPoints.get(i).index;
        }
        return indexStr;
    }

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            clearSelectPointStatus();
        }
    };

    /**
     * 设置错误状态
     */
    private void setAllSelectPointError(){
        for (int i = 0; i < mSelectPoints.size(); i++){
            mSelectPoints.get(i).status = Point.STATUS_ERROR;
        }
        invalidate();
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mHandler.sendEmptyMessage(Point.STATUS_ERROR);
            }
        },1000);
    }

    /**
     * 设置通过状态
     */
    private void setAllSelectPointPass(){
        for (int i = 0; i < mSelectPoints.size(); i++){
            mSelectPoints.get(i).status = Point.STATUS_PASS;
        }
        invalidate();
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mHandler.sendEmptyMessage(Point.STATUS_PASS);
            }
        },1000);
    }

    /**
     * 清除选中点的状态
     */
    private void clearSelectPointStatus(){
        for (int i = 0;i<mPoints.length;i++) {
            for (int j = 0;j<mPoints[i].length;j++) {
                // 循环绘制九个点
                mPoints[i][j].status = Point.STATUS_NORMAL;
            }
        }
        mSelectPoints.clear();
        invalidate();
    }


    //获取当前触摸位置的点
    private Point getTouchPoint(float x,float y){
        for (int i = 0;i<mPoints.length;i++) {
            for (int j = 0;j<mPoints[i].length;j++) {
                // 循环遍历九个点
                Point point = mPoints[i][j];

                if (x>point.centerX-mDotRadius && x < point.centerX+mDotRadius && y>point.centerY-mDotRadius && y<point.centerY+mDotRadius){
                    return point;
                }
            }
        }
        return null;
    }

    private void drawDot(Canvas canvas) {
        for (int i = 0;i<mPoints.length;i++) {
            for (int j = 0;j<mPoints[i].length;j++) {
                // 循环绘制九个点
                Point point = mPoints[i][j];
                if (Point.STATUS_NORMAL == point.status) {
                    canvas.drawCircle(point.centerX, point.centerY, mInnerDotRadius, mInnerNormalPaint);
                    canvas.drawCircle(point.centerX, point.centerY, mDotRadius, mOuterNormalPaint);
                }else if(Point.STATUS_PRESSED == point.status){
                    canvas.drawCircle(point.centerX, point.centerY, mInnerDotRadius, mInnerPressedPaint);
                    canvas.drawCircle(point.centerX, point.centerY, mDotRadius, mOuterPressedPaint);
                }else if(Point.STATUS_ERROR == point.status){
                    canvas.drawCircle(point.centerX, point.centerY, mInnerDotRadius, mInnerErrorPaint);
                    canvas.drawCircle(point.centerX, point.centerY, mDotRadius, mOuterErrorPaint);
                }else if(Point.STATUS_PASS == point.status){
                    canvas.drawCircle(point.centerX, point.centerY, mInnerDotRadius, mInnerPassPaint);
                    canvas.drawCircle(point.centerX, point.centerY, mDotRadius, mOuterPassPaint);
                }
            }
        }
    }

    class Point{
        //默认状态
        public static final int STATUS_NORMAL = 1;
        //选择状态
        public static final int STATUS_PRESSED = 2;
        //错误状态
        public static final int STATUS_ERROR = 3;
        //通过状态
        public static final int STATUS_PASS = 4;
        //中心点x
        int centerX;
        //中心点y
        int centerY;
        //下标,就是我们定义的密码1-9
        int index;
        //当前点的状态
        int status = STATUS_NORMAL;

        public Point(int centerX, int centerY, int index) {
            this.centerX = centerX;
            this.centerY = centerY;
            this.index = index;
        }
    }
}

关于这里面颜色和宽度的自定义大家可以自己去修改下,我会在后面的时间修改后上传到github上,链接下次上传后会写在这里.

就说这么多了,谢谢.

Github源码地址 源码

自定义属性已经添加了,大家可以去github查看源代码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值