Android手势密码

一、简介

1、本篇博文给大家介绍手势密码的绘制,首先看一下效果,如下:分为设置手势密码与验证手势密码;


二、结构分析:


如上图所示,我们在创建View之前,需将基本思路理清,我大致介绍一下我的理解:

1、首先大家要有一个思路就是,要创建这个View,必须得有3X3一共9个圆,然后就是排列这9个圆;

2、要进行绘制9个圆的话,要获取整个控件的宽高,(在本列中我是直接获取布局控件的宽高,没有限制最小值,这个大家看自己的需求,为了追求更完美可以限制绘制的大小

3、通过获取的宽高,设置单个圆的半径,已经每个圆的中心点;

如此,可将9个3X3的圆画出,后面绘制部分我将以代码的形式列出;


三、代码分析;

1、控件的参数属性:

private int startX;//手指开始点击位置X
private int startY;//手指开始点击位置Y
private int endX;//手指滑动点X坐标
private int endY;//手指滑动点Y坐标
private Paint linePaint;//画线笔
private Paint circlePaint;//画圆笔
private int viewWidth;//控件宽度
private int viewHeight;//控件高度
private int radius;//圆半径

private PointF[][] centerCxCy;//各个圆的圆心二维数组
private int[][] data;//对应各个圆的密码数,二维数组
private boolean[][] selected;//对应各个圆是否已选中
private List<PointF> selPointFs;//已选中圆的中心点集合
private String lockPwd = "";//密码字符串
private String password = null;
private boolean isPressedDown = false;//是否有手指按下
private int red;//颜色值
private int green;//颜色值
private int blue;//颜色值
private final int flag = 3;//矩阵大小
private boolean isError = false;//在验证密码时,判断输入密码是否错误
private OnGesturePwdListener gesturePwdListener;//自定义回调监听
private Thread errorThread;// 输错手势密码后延时线程

2、构造函数、初始化参数

public GesturePwdView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    init();
}
private void init() {
    linePaint = new Paint();
    linePaint.setStrokeWidth(5);// 画线笔宽度
    linePaint.setStyle(Style.FILL);// 画笔样式铺满
    linePaint.setAntiAlias(true);// 抗锯齿

    circlePaint = new Paint();
    circlePaint.setStrokeWidth(ScreenUtil.dip2px(getContext(), 1));
    circlePaint.setAntiAlias(true);
    circlePaint.setStyle(Style.STROKE); // 画笔样式空心

    centerCxCy = new PointF[flag][flag];
    data = new int[flag][flag];
    selected = new boolean[flag][flag];
    selPointFs = new ArrayList<PointF>();

    initData();
}
// 赋值data数组
private void initData() {
    // TODO Auto-generated method stub
    int num = 1;
    for (int i = 0; i < flag; i++) {
        for (int j = 0; j < flag; j++) {
            data[j][i] = num;
            num++;
        }
    }
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    if (changed) {
        viewWidth = getWidth();
        viewHeight = getHeight();
        setRadius();
    }
    super.onLayout(changed, left, top, right, bottom);
}
// 设置半径和圆心
private void setRadius() {
    int w = viewWidth / 3;
    int h = viewHeight / 3;
    if (w <= h) {
        radius = w / 3;
    } else {
        radius = h / 3;
    }

    for (int i = 0; i < flag; i++) {
        for (int j = 0; j < flag; j++) {
            PointF cPointF = new PointF();
            cPointF.x = (i * w) + w / 2;
            cPointF.y = (j * h) + h / 2;
            centerCxCy[i][j] = cPointF;
        }
    }
}

三、配置方法(判断点是否在圆内、获取该圆所代表的值、清除选中记录、不齐两圆中间的圆等)

// 还原为初始状态
private void setIsErrorDelayOver() {
    lockPwd = "";
    selPointFs.clear();
    clearSelected();
    invalidate();
}

// 清除选中记录
private void clearSelected() {
    for (int i = 0; i < flag; i++) {
        for (int j = 0; j < flag; j++) {
            selected[i][j] = false;
        }
    }
}

// 判断是否在某个圆内 公式 :根号(x1-x2)(x1-x2)+(y1-y2)(y1-y2)
private boolean isInCircle(PointF p, int x, int y) {
    int distance = (int) Math.sqrt((p.x - x) * (p.x - x) + (p.y - y)
            * (p.y - y));
    if (distance <= radius) {
        return true;
    }
    return false;
}

// 获取选中的单个点的值
private int getLockSinglePwdData(int x, int y) {
    for (int i = 0; i < flag; i++) {
        for (int j = 0; j < flag; j++) {
            PointF cPointF = centerCxCy[i][j];
            if (isInCircle(cPointF, x, y)) { // 判断是否在圆内
                if (!selected[i][j]) {
                    selected[i][j] = true;
                    selPointFs.add(cPointF);
                    return data[i][j];
                }
            }
        }
    }
    return 0;
}
public String getPassword() {
    return password;
}

// 设置原手势密码
public void setPassword(String password) {
    this.password = password;
}
// 补充圆,在一个横线内的3个点,其中两边的俩个点选中时,中间的点也默认选中
//大家可根据自己的需求来决定是否使用这个方法
private String setReplenish(String s) {
    if (!s.contains("5")) {
        if (s.contains("46")) {
            selected[1][1] = true;
            return addString(s, '4', "5");
        } else if (s.contains("64")) {
            selected[1][1] = true;
            return addString(s, '6', "5");
        } else if (s.contains("28")) {
            selected[1][1] = true;
            return addString(s, '2', "5");
        } else if (s.contains("82")) {
            selected[1][1] = true;
            return addString(s, '8', "5");
        } else if (s.contains("19")) {
            selected[1][1] = true;
            return addString(s, '1', "5");
        } else if (s.contains("91")) {
            selected[1][1] = true;
            return addString(s, '9', "5");
        } else if (s.contains("37")) {
            selected[1][1] = true;
            return addString(s, '3', "5");
        } else if (s.contains("73")) {
            selected[1][1] = true;
            return addString(s, '7', "5");
        }
    }

    if (!s.contains("2")) {
        if (s.contains("13")) {
            selected[1][0] = true;
            return addString(s, '1', "2");
        } else if (s.contains("31")) {
            selected[1][0] = true;
            return addString(s, '3', "2");
        }
    }
    if (!s.contains("8")) {
        if (s.contains("79")) {
            selected[1][2] = true;
            return addString(s, '7', "8");
        } else if (s.contains("97")) {
            selected[1][2] = true;
            return addString(s, '9', "8");
        }
    }
    if (!s.contains("4")) {
        if (s.contains("17")) {
            selected[0][1] = true;
            return addString(s, '1', "4");
        } else if (s.contains("71")) {
            selected[0][1] = true;
            return addString(s, '7', "4");
        }
    }
    if (!s.contains("6")) {
        if (s.contains("39")) {
            selected[2][1] = true;
            return addString(s, '3', "6");
        } else if (s.contains("93")) {
            selected[2][1] = true;
            return addString(s, '9', "6");
        }
    }
    return s;
}

private String addString(String s, char f, String d) {
    char[] a = s.toCharArray();
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < a.length; i++) {
        sb.append(a[i]);
        if (a[i] == f) {
            sb.append(d);
        }
    }
    return sb.toString();
}
四、绘制图形;

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    linePaint.setStrokeWidth(2 * radius / 3);
    setDraw(red, green, blue, canvas);
}

// 绘制图形
private void setDraw(int red, int green, int blue, Canvas canvas) {
    for (int i = 0; i < flag; i++) {
        for (int j = 0; j < flag; j++) {
            PointF cPointF = centerCxCy[i][j];
            if (selected[i][j]) {// 选中
                circlePaint.setColor(Color.rgb(red, green, blue));
                linePaint.setColor(Color.argb(255, red, green, blue));
                canvas.drawCircle(cPointF.x, cPointF.y, radius / 3,
                        linePaint);// 画实心圆
            } else {
                circlePaint.setColor(Color.argb(180, 255, 255, 255));// 没有选中外圆颜色
            }
            canvas.drawCircle(cPointF.x, cPointF.y, radius, circlePaint);// 画外圆
        }
    }
    linePaint.setColor(Color.argb(96, red, green, blue));// 画线
    if (isPressedDown) {
        for (int i = 0; i < selPointFs.size() - 1; i++) {// 画已选中圆之间的路径
            PointF prePointF = selPointFs.get(i);
            PointF curPointF = selPointFs.get(i + 1);
            canvas.drawLine(prePointF.x, prePointF.y, curPointF.x,
                    curPointF.y, linePaint);
        }
        if (!isError) {
            if (selPointFs.size() > 0) {
                PointF cPointF = selPointFs.get(selPointFs.size() - 1); // 最后一个选中圆中点
                canvas.drawLine(cPointF.x, cPointF.y, endX, endY, linePaint);
            }
        }
    }
}
五、设置监听:

/**
 * 设置监听
 *
 * @author yang.shen
 */
public interface OnGesturePwdListener {
    // 当密码改变
    public void onChangePin(GesturePwdView gesturePwdView, String pwd);

    // 比较密码成功
    public void onComparePwdSuccess(GesturePwdView gesturePwdView,
                                    String pwd);

    // 比较密码失败
    public void onComparePwdFail(GesturePwdView gesturePwdView, String pwd);
}

public void setOnGesturePwdListener(OnGesturePwdListener gesturePwdListener) {
    this.gesturePwdListener = gesturePwdListener;
}

六、设置滑动状态

@Override
public boolean onTouchEvent(MotionEvent event) {
    // TODO Auto-generated method stub

    int pin = 0;
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            if (isError) {
                errorThread.interrupt();
            }
            endX = (int) event.getX();
            endY = (int) event.getY();
            lockPwd = "";
            selPointFs.clear();
            isPressedDown = true;

            startX = (int) event.getX();
            startY = (int) event.getY();
            pin = getLockSinglePwdData(startX, startY);
            red = 167;
            green = 169;
            blue = 175;
            if (pin > 0) {
                isError = false;
                lockPwd += pin;
                invalidate();
            }
            break;
        case MotionEvent.ACTION_MOVE:
            endX = (int) event.getX();
            endY = (int) event.getY();
            red = 167;
            green = 169;
            blue = 175;
            pin = getLockSinglePwdData(endX, endY);
            if (pin > 0) {
                isError = false;
                lockPwd += pin;
            }
            lockPwd = setReplenish(lockPwd);
            invalidate();
            break;
        case MotionEvent.ACTION_UP:
            endX = (int) event.getX();
            endY = (int) event.getY();
            isPressedDown = false;
            if (password == null || "".equals(password)) {
                red = 167;
                green = 169;
                blue = 175;
                gesturePwdListener.onChangePin(this, lockPwd);
                clearSelected();
                invalidate();
            } else if (password.equals(lockPwd)) {
                red = 167;
                green = 169;
                blue = 175;
                gesturePwdListener.onComparePwdSuccess(this, lockPwd);
                clearSelected();
                invalidate();
            } else if (!password.equals(lockPwd)) {
                isPressedDown = true;
                isError = true;
                red = 255;
                green = 0;
                blue = 0;
                invalidate();
                gesturePwdListener.onComparePwdFail(this, lockPwd);
                inputError();
            }
            break;
        case MotionEvent.ACTION_CANCEL:

            break;
        default:
            break;
    }
    return true;
}

七、判断密码的错对,显示错误状态3秒

private Handler mHandler = new Handler(new Handler.Callback() {

    @Override
    public boolean handleMessage(Message msg) {
        // TODO Auto-generated method stub
        setIsErrorDelayOver();
        return false;
    }
});

// 输错密码后延时3秒清空状态
private void inputError() {
    errorThread = new Thread() {
        @Override
        public void run() {
            try {
                sleep(3000);
                mHandler.sendEmptyMessage(0);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                mHandler.sendEmptyMessage(0);
                e.printStackTrace();
            }
        }
    };
    errorThread.start();
}

以上,代报错功能的手势密码就可以使用了,稍后我会将代码上传,谢谢大家


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值