天天看點

Android應用 手勢密碼的實作(二)

本文基于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 的執行順序,想加深了解的可以暫時保持原先的順序看看效果。

 流程圖:

Android應用 手勢密碼的實作(二)

代碼:

@Override
protected 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 時不進行繪制,和流程圖一緻):

@Override
public 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);// 調用
           

運作結果,隻有錯誤時才顯示路徑:

Android應用 手勢密碼的實作(二)
Android應用 手勢密碼的實作(二)

二、自動重置

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,現在可以看看自動重置的效果了:

Android應用 手勢密碼的實作(二)

三、結:

這篇文章就到這裡了,下一篇文章會做最後的改動,主要是增加一個功能,然後點選事件的邏輯需要做些修改:

1、加一個設定初始密碼的功能

2、讓手勢單點可以生效

3、自動補充軌迹(例如選中第一排的1位和3位時2位也能自動選中)