本文基于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位也能自动选中)