Android应用 手势密码的实现(二)

来源:互联网 发布:android数独游戏源码 编辑:程序博客网 时间:2024/06/05 18:19

本文基于Hongyang大神的博客:http://blog.csdn.net/lmj623565791/article/details/36236113

转载请注明来源:http://blog.csdn.net/u013258802/article/details/53063691

上一篇界面修改的文章:http://blog.csdn.net/u013258802/article/details/52959513


本文的目的如下:

1、隐藏手势轨迹

2、自动重置


一、隐藏手势轨迹

1、先看 GestureLockView:

隐藏手势轨迹需要 GestureLockView 一直保持 STATUS_NO_FINGER 的绘制状态,除非用户输入错误。

所以我们就需要获取手势正误的状态以及知道是否绘制路径:

// 用于设置是否绘制轨迹(箭头和内圆)private boolean showPath = true;// 默认显示// 用于标记答案是否正确private boolean isAnswerRight;public void setIsAnswerRight(boolean isRight) {    isAnswerRight = isRight;}

OnDraw内 switch 块内的逻辑需要改变。

当 showPath 为 false 时,我们希望 FINGER_ON 的绘制代码绘制出 NO_FINGER 的样式,把 绘制代码和break 包在 if (showPath) {}  中即可实现这个效果。

当 showPath 为 false 并且 isAnswerRight 为 true 时,我们也希望 FINGER_UP 的绘制代码绘制出 NO_FINGER 的样式,那么同上。

根据上面的设计,我们必须保证标识最多的状态放在最上面执行,否则部分标识更新慢的话就会导致乱序,语言说明不便理解,我们下一步要更换原代码里 FINGER_UP 和 FINGER_ON 的执行顺序,想加深理解的可以暂时保持原先的顺序看看效果。

 流程图:


代码:

@Overrideprotected void onDraw(Canvas canvas){    switch (mCurrentStatus)    {        case STATUS_FINGER_UP:            // 在手指抬起后,答案错误一定会进行绘制,如果答案正确那么再判断是否显示路径            if (!isAnswerRight || showPath) {                // 绘制外圆                mPaint.setColor(mColorFingerUp);                mPaint.setStyle(Paint.Style.STROKE);                mPaint.setStrokeWidth(mStrokeWidth);                canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint);                // 绘制内圆                mPaint.setStyle(Paint.Style.FILL);                canvas.drawCircle(mCenterX, mCenterY, mRadius * mInnerCircleRadiusRate, mPaint);                // 绘制箭头                drawArrow(canvas);                break;            }        case STATUS_FINGER_ON:            // 在画手势密码时判断是否显示路径            if (showPath) {                // 绘制外圆                mPaint.setStyle(Paint.Style.STROKE);                mPaint.setColor(mColorFingerOn);                mPaint.setStrokeWidth(mStrokeWidth);                canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint);                // 绘制内圆                mPaint.setStyle(Paint.Style.FILL);                canvas.drawCircle(mCenterX, mCenterY, mRadius * mInnerCircleRadiusRate, mPaint);                break;            }        case STATUS_NO_FINGER:            // 绘制外圆            mPaint.setStyle(Paint.Style.STROKE);            mPaint.setColor(mColorNoFingerOutter);            mPaint.setStrokeWidth(mStrokeWidth / 2);            canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint);            // 绘制内圆            mPaint.setStyle(Paint.Style.FILL);            mPaint.setColor(mColorNoFingerInner);            canvas.drawCircle(mCenterX, mCenterY, mRadius * mInnerCircleRadiusRate, mPaint);            break;    }}

我们需要在View的每次 onDraw 前更新 showPath 的状态,否则设置无法及时生效,因此现在要改 setMode 方法,增加一个 showPath 参数:

public void setMode(Mode mode, boolean showPath) {    this.mCurrentStatus = mode;    this.showPath = showPath;    invalidate();}

2、接下来就是GestureLockViewGroup:

首先添加变量和set方法:

// 记录答案是否正确private boolean isAnswerRight = true;// 默认显示轨迹private boolean showPath = true;// 对外公开的set方法public void setShowPath(boolean showPath) {    this.showPath = showPath;}

然后调用setMode的地方就要增加参数了,这个就不多说了,例:

mGestureLockViews[i].setMode(GestureLockView.Mode.STATUS_NO_FINGER, showPath);

在手指抬起的事件中我们需要获取答案是否正确,然后在dispacthDraw()方法中依据标识状态避开路径的绘制。

ACTION_UP事件部分(获取 isAnswerRight 的状态):

// 回调是否成功if (mOnGestureLockViewListener != null && mChoose.size() > 0) {    isAnswerRight = checkAnswer();// 获取状态    setViewColor(isAnswerRight);    mOnGestureLockViewListener.onGestureEvent(isAnswerRight);    if (this.mTryTimes == 0) {        ...    }

DispatchDraw方法部分(ShowPath为 false 并且答案为 true 时不进行绘制,和流程图一致):

@Overridepublic void dispatchDraw(Canvas canvas){    super.dispatchDraw(canvas);    if (!showPath && isAnswerRight) {        return;    }    //绘制GestureLockView间的连线    if (mPath != null)    {...

当然这里还有点小问题,就是在画手势(ACTION_MOVE)过程中,会因为上一次的错误答案导致 isAnswerRight 为 false 并进入绘制流程,所以需要在reset时把 isAnswerRight 初始化为 true:

private void reset(){    mChoose.clear();    mPath.reset();    isAnswerRight = true;// 重置    for (GestureLockView gestureLockView : mGestureLockViews)    {        gestureLockView.setMode(GestureLockView.Mode.STATUS_NO_FINGER, showPath);        gestureLockView.setArrowDegree(-1);    }}

最后我们在changeItemMode 方法中调用之前为GestureLockView 添加的 setIsAnswerRight() 方法:

private void changeItemMode(){    for (GestureLockView gestureLockView : mGestureLockViews)    {        if (mChoose.contains(gestureLockView.getId()))        {            gestureLockView.setViewColor(mFingerUpColor);            gestureLockView.setIsAnswerRight(isAnswerRight);// 调用            gestureLockView.setMode(GestureLockView.Mode.STATUS_FINGER_UP, showPath);        }    }}

好了,现在在Activity中调用setShowPath()方法:

mGesture = (GestureLockViewGroup) findViewById(R.id.gesture_lock_view_group);mGesture.setAnswer(...);mGesture.setShowPath(false);// 调用

运行结果,只有错误时才显示路径:

  


二、自动重置

GestureLockViewGroup的 ACTION_DOWN 触摸事件里用到了reset(),可见落指时页面内容会重置,但我希望画完手势过一秒左右自动清空,所以写个方法delayReset(),delayReset()会延时1000ms执行reset()。

private Handler mHandler = new Handler();private Runnable mRunnable = new Runnable() {    @Override    public void run() {        reset();        invalidate();    }};private void delayReset() {    mHandler.postDelayed(mRunnable, 1000);// 加入线程队列}

这里需要留心在延时的时候用户点击界面的情况,所以在 reset() 方法执行时,如果存在延时操作则把延时的操作取消:

private void reset(){    if (mHandler != null && mRunnable != null) {        mHandler.removeCallbacks(mRunnable);// 从线程队列中移除    }    mChoose.clear();    mPath.reset();    ...

然后在 ACTION_UP 里 break 之前调用:

case MotionEvent.ACTION_UP:    ...    // 计算每个元素中箭头需要旋转的角度    for (...)    {        ...    }    delayReset();// 调用    break;

OK,现在可以看看自动重置的效果了:




三、结:

这篇文章就到这里了,下一篇文章会做最后的改动,主要是增加一个功能,然后点击事件的逻辑需要做些修改:

1、加一个设置初始密码的功能

2、让手势单点可以生效

3、自动补充轨迹(例如选中第一排的1位和3位时2位也能自动选中)
0 0
原创粉丝点击