本周项目需要做一个简单的功能就是双击图片放大,这是一个简单的功能,网上一搜有很多,大多都是使用的GestureDetector的listener进行监听,但是自己看了一下GestureDetector的代码决定自己来实现,首先是先查看GestureDetector的ontouch代码如下:由于只涉及到MotionEvent.ACTION_DOWN:和MotionEvent.ACTION_MOVE:,因此现在只讨论这两个方法。
public boolean onTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 0);
}
final int action = ev.getAction();
final float y = ev.getY();
final float x = ev.getX();
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
boolean handled = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
if (mIgnoreMultitouch) {
// Multitouch event - abort.
cancel();
}
break;
case MotionEvent.ACTION_POINTER_UP:
// Ending a multitouch gesture and going back to 1 finger
if (mIgnoreMultitouch && ev.getPointerCount() == 2) {
int index = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0;
mLastMotionX = ev.getX(index);
mLastMotionY = ev.getY(index);
mVelocityTracker.recycle();
mVelocityTracker = VelocityTracker.obtain();
}
break;
case MotionEvent.ACTION_DOWN:
if (mDoubleTapListener != null) {
//是否有以前触发的点击事件,如果有点击事件就将其移除
boolean hadTapMessage = mHandler.hasMessages(TAP);
if (hadTapMessage) mHandler.removeMessages(TAP);
//判断是否符合双击的条件,根据hadTapMessage来判断当前是第二次点击
if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
//如果符合就将mIsDoubleTapping设置为true,然后触发onDoubleTap和onDoubleTapEvent事件
// This is a second tap
mIsDoubleTapping = true;
// Give a callback with the first tap of the double-tap
handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
// Give a callback with down event of the double-tap
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else {
// This is a first tap 如果不符合双击的条件,就触发单击事件注意DOUBLE_TAP_TIMEOUT这个很重要
mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
}
}
mLastMotionX = x;
mLastMotionY = y;
if (mCurrentDownEvent != null) {
mCurrentDownEvent.recycle();
}
//设置为当前的事件
mCurrentDownEvent = MotionEvent.obtain(ev);
//初始设置mAlwaysInTapRegion(是否一直点击同一个位置 ),mAlwaysInBiggerTapRegion(是否在更大的范围内点击)和mStillDown(是否按下就不动了)都为true:
mAlwaysInTapRegion = true;
mAlwaysInBiggerTapRegion = true;
mStillDown = true;
mInLongPress = false;
if (mIsLongpressEnabled) {
mHandler.removeMessages(LONG_PRESS);
mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
+ TAP_TIMEOUT + LONGPRESS_TIMEOUT);
}
mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
handled |= mListener.onDown(ev);
break;
case MotionEvent.ACTION_MOVE:
if (mInLongPress || (mIgnoreMultitouch && ev.getPointerCount() > 1)) {
break;
}
final float scrollX = mLastMotionX - x;
final float scrollY = mLastMotionY - y;
if (mIsDoubleTapping) {
// Give the move events of the double-tap 如果是双击事件触发onDoubleTapEvent
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mAlwaysInTapRegion) {
final int deltaX = (int) (x - mCurrentDownEvent.getX());
final int deltaY = (int) (y - mCurrentDownEvent.getY());
int distance = (deltaX * deltaX) + (deltaY * deltaY);
//如果是在点击同一个位置,并且符合滑动的距离,触发onscroll事件
if (distance > mTouchSlopSquare) {
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastMotionX = x;
mLastMotionY = y;
mAlwaysInTapRegion = false;
mHandler.removeMessages(TAP);
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
}
if (distance > mBiggerTouchSlopSquare) {
//对mAlwaysInBiggerTapRegion进行重新定义
mAlwaysInBiggerTapRegion = false;
}
} else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
//如果现在一直在滑动中,触发onscroll事件
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastMotionX = x;
mLastMotionY = y;
}
break;
case MotionEvent.ACTION_UP:
mStillDown = false;
MotionEvent currentUpEvent = MotionEvent.obtain(ev);
if (mIsDoubleTapping) {
// Finally, give the up event of the double-tap 如果是双击触发onDoubleTapEvent事件
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mInLongPress) {
mHandler.removeMessages(TAP);
mInLongPress = false;
} else if (mAlwaysInTapRegion) {
handled = mListener.onSingleTapUp(ev);
} else {
//判断是不是要触发onFling方法
// A fling must travel the minimum tap distance
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
final float velocityY = velocityTracker.getYVelocity();
final float velocityX = velocityTracker.getXVelocity();
if ((Math.abs(velocityY) > mMinimumFlingVelocity)
|| (Math.abs(velocityX) > mMinimumFlingVelocity)){
handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
}
}
if (mPreviousUpEvent != null) {
mPreviousUpEvent.recycle();
}
// Hold the event we obtained above - listeners may have changed the original.
mPreviousUpEvent = currentUpEvent;
mVelocityTracker.recycle();
mVelocityTracker = null;
mIsDoubleTapping = false;
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
break;
case MotionEvent.ACTION_CANCEL:
cancel();
break;
}
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 0);
}
return handled;
}
下面看handler的方法:
private class GestureHandler extends Handler {
GestureHandler() {
super();
}
GestureHandler(Handler handler) {
super(handler.getLooper());
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_PRESS:
mListener.onShowPress(mCurrentDownEvent);
break;
case LONG_PRESS:
dispatchLongPress();
break;
case TAP:
// If the user's finger is still down, do not count it as a tap 判断是否要触发点击事件
if (mDoubleTapListener != null && !mStillDown) {
mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
}
break;
default:
throw new RuntimeException("Unknown message " + msg); //never
}
}
}
//判断双击的条件
private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
MotionEvent secondDown) {
if (!mAlwaysInBiggerTapRegion) {
return false;
}
if (secondDown.getEventTime() - firstUp.getEventTime() > DOUBLE_TAP_TIMEOUT) {
return false;
}
int deltaX = (int) firstDown.getX() - (int) secondDown.getX();
int deltaY = (int) firstDown.getY() - (int) secondDown.getY();
return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);
}
双击事件的原理是:首先在down按下的时候,查找是否有上次没有消耗的点击事件,如果有取消,同时判断是否符合双击的条件(时间上和位置上)。通俗地讲就是如果第二次点击在DOUBLE_TAP_TIMEOUT的
时间的内点击,此时就会取消上次的点击message,同时根据条件判定为双击事件;
单击事件;两次点击的时间间隔超过DOUBLE_TAP_TIMEOUT即可;
长按事件:第一次down的时候出发长按事件但是mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()+ TAP_TIMEOUT + LONGPRESS_TIMEOUT);,但是实在
mCurrentDownEvent.getDownTime()+ TAP_TIMEOUT + LONGPRESS_TIMEOUT时间后开始发送消息,同时分别在move和up事件里面进行判断,如果分别出发了里面的事件,就将长按事件清除;
滑动事件:是在up里面出发,比较简单不做讲述
在学习的时候看了一下http://blog.youkuaiyun.com/lonelyroamer/article/details/7558313这篇文章,在此表示感谢