本文基于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 的執行順序,想加深了解的可以暫時保持原先的順序看看效果。
流程圖:
代碼:
@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);// 調用
運作結果,隻有錯誤時才顯示路徑:
二、自動重置
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位也能自動選中)