GestureDetector是手势检测器,在自定义View中经常需要用到。关于GestureDetector的使用,网上有很多的资源。但对于GestureDetector源码的分析却很少,且每个Listener触发的条件写得不够详细,后面自己详细查看了源码,为了便于自己查看和加深印象,于是将这些信息记录下来。
GestureDetector里面定义了两个接口,onGestureListener和OnDoubleTapListener,分别如下:
public interface OnGestureListener {
boolean onDown(MotionEvent e);
void onShowPress(MotionEvent e);
boolean onSingleTapUp(MotionEvent e);
boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
void onLongPress(MotionEvent e);
boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
}
public interface OnDoubleTapListener {
boolean onSingleTapConfirmed(MotionEvent e);
boolean onDoubleTap(MotionEvent e);
boolean onDoubleTapEvent(MotionEvent e);
}
在定义GestureDetector对象时,为了使得GestureDetector内部接口对应的方法有意义,需要在onTouchEvent方法中调用GestureDetector的onTouchEvent(MotionEvent ev)方法。下面我们重点看一下这个方法,逐个查看上述对外提供的接口调用的情况,代码如下(源码Android5.1):
public boolean onTouchEvent(MotionEvent ev) {
......
case MotionEvent.ACTION_DOWN:
if (mDoubleTapListener != null) {
// 判断是否需要移除WHAT为TAP的消息,该消息用于判断是否触发
//DoubleTapListener.onSingleTapConfirmed
boolean hadTapMessage = mHandler.hasMessages(TAP);
if (hadTapMessage) mHandler.removeMessages(TAP);
// 上次的down和up事件都存在,且两次down事件的距离及第一次
// up距离第二次down的时间都符合触发二次点击的条件,则判断为触发了二次点击
if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
// 设置mIsDoubleTapping为true,并调用mDoubleTapListener对应的监听,
// mCurrentDownEvent为第一次的down事件
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 {
// 延迟发送TAP消息,判断触发DoubleTapListener.onSingleTapConfirmed
// 事件,该事件必须mStillDown为false才会触发,即已经执行了up事件
mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
}
}
mDownFocusX = mLastFocusX = focusX;
mDownFocusY = mLastFocusY = focusY;
if (mCurrentDownEvent != null) {
mCurrentDownEvent.recycle();
}
// 记录当前的Down事件
mCurrentDownEvent = MotionEvent.obtain(ev);
mAlwaysInTapRegion = true;
mAlwaysInBiggerTapRegion = true;
mStillDown = true;
mInLongPress = false;
mDeferConfirmSingleTap = false;
// 如果支持长按,则距离当前时间(TAP_TIMEOUT + LONGPRESS_TIMEOUT,默认为600ms)
// 毫秒触发LONG_PRESS消息,该消息执行dispatchLongPress()方法,
// 调用OnGestureListener的onLongPress(mCurrentDownEvent)方法
if (mIsLongpressEnabled) {
mHandler.removeMessages(LONG_PRESS);
mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
+ TAP_TIMEOUT + LONGPRESS_TIMEOUT);
}
// 距离当前时间TAP_TIMEOUT毫秒,发送SHOW_PRESS消息,
// 执行OnGestureListener的onShowPress(mCurrentDownEvent )
mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
// 调用OnGestureListener的onDown(ev)
handled |= mListener.onDown(ev);
break;
case MotionEvent.ACTION_MOVE:
if (mInLongPress) {
break;
}
final float scrollX = mLastFocusX - focusX;
final float scrollY = mLastFocusY - focusY;
if (mIsDoubleTapping) {
// 调用DoubleTapListener.onDoubleTapEvent(ev),
// 说明onDoubleTapEvent很可能会调用多次
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mAlwaysInTapRegion) {
// mAlwaysInTapRegion在down事件设置为true,而当条件以下的
// if条件执行之后,该值将设置为false。说明只要不是执行双击,
// 那么以下的判断一定会执行一次
final int deltaX = (int) (focusX - mDownFocusX);
final int deltaY = (int) (focusY - mDownFocusY);
int distance = (deltaX * deltaX) + (deltaY * deltaY);
// 如果滑动的距离大于mTouchSlopSquare,该值默认为8*8,
// 则执行OnGestureListener的onScroll,第一个参数为down,
// 第二个参数为当前的ev,同时移除TAP、SHOW_PRESS、LONG_PRESS的消息
if (distance > mTouchSlopSquare) {
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastFocusX = focusX;
mLastFocusY = focusY;
mAlwaysInTapRegion = false;
mHandler.removeMessages(TAP);
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
}
// 如果移动的记录大于mDoubleTapTouchSlopSquare,
// 则mAlwaysInBiggerTapRegion为false。
// 当mAlwaysInBiggerTapRegion为false,表示不可能在执行双击事件,
// 因为点击之后移动了比较大的距离
if (distance > mDoubleTapTouchSlopSquare) {
mAlwaysInBiggerTapRegion = false;
}
} else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
// 执行OnGestureListener的onScroll
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastFocusX = focusX;
mLastFocusY = focusY;
}
break;
case MotionEvent.ACTION_UP:
mStillDown = false;
MotionEvent currentUpEvent = MotionEvent.obtain(ev);
if (mIsDoubleTapping) {
// 如果是双击事件,调用DoubleTapListener.onDoubleTapEvent(ev),
// 传入的ev为up的事件
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mInLongPress) {
// 如果是长按,则移除TAP
mHandler.removeMessages(TAP);
mInLongPress = false;
} else if (mAlwaysInTapRegion) {
// 如果mAlwaysInTapRegion为ture,表示move的距离很短,
// 则执行OnGestureListener的onSingleTapUp(ev)
handled = mListener.onSingleTapUp(ev);
// 如果mDeferConfirmSingleTap为true,该值只有在TAP消息中才有
// 可能被置为true,则执行DoubleTapListener.onSingleTapConfirmed(ev)。
// 结合TAP消息的处理,只要不是双击、长按,且点击后滑动的距离很短,
// 则一定会执行DoubleTapListener.onSingleTapConfirmed(ev)
if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
mDoubleTapListener.onSingleTapConfirmed(ev);
}
} else {
// 判断当前滑动的速度,确认是否执行OnGestureListener
// 的onFling()方法,第一个参数为down事件,第二个参数为up事件
final VelocityTracker velocityTracker = mVelocityTracker;
final int pointerId = ev.getPointerId(0);
velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
final float velocityY = velocityTracker.getYVelocity(pointerId);
final float velocityX = velocityTracker.getXVelocity(pointerId);
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;
if (mVelocityTracker != null) {
// This may have been cleared when we called out to the
// application above.
mVelocityTracker.recycle();
mVelocityTracker = null;
}
mIsDoubleTapping = false;
mDeferConfirmSingleTap = false;
// 移除SHOW_PRESS、LONG_PRESS消息
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;
}
GestureDetector中所有Listener的调用情况如上分析,接下来做下总结:
onDown(MotionEvent e):手指按下之后触发,ev为down事件,一定会执行,如果是双击的话会调用两次;
onShowPress(MotionEvent e):如果down之后,move距离很小且过了TAP_TIMEOUT(默认为100ms)还没有松开,则会触发。ev为down事件;
onSingleTapUp(MotionEvent e):如果down之后,不触发双击和长按,且move的距离很小,则触发该事件。ev为Up事件;
onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY):如果不是双击,且有滑动,则多次触发该事件。e1为down事件,e2为当前move的事件;
onLongPress(MotionEvent e):如果down之后move的距离很小,且过了600ms,则会触发;
onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY):如果不是双击,且滑动,up之后速度大于系统最低速率时触发,e1为down事件,e2为up事件;
onSingleTapConfirmed(MotionEvent e):如果down之后,过了300ms不执行双击且滑动的距离很小,则触发;
onDoubleTap(MotionEvent e):如果两次down的时间比较近,且上次up和本次down的位置接近,上次点击滑动的距离很小,则表示触发了双击,在down事件中执行该方法,只执行一次;
onDoubleTapEvent(MotionEvent e):如果是双击事件,则执行该方法。down、move和up都会执行;