GestureDetectorpublic class GestureDetector extends Object Detects various gestures and events using the supplied {@link MotionEvent}s.
The {@link OnGestureListener} callback will notify users when a particular
motion event has occurred. This class should only be used with {@link MotionEvent}s
reported via touch (don't use for trackball events).
To use this class:
- Create an instance of the {@code GestureDetector} for your {@link View}
- In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
{@link #onTouchEvent(MotionEvent)}. The methods defined in your callback
will be executed when the events occur.
|
Fields Summary |
---|
private int | mBiggerTouchSlopSquare | private int | mTouchSlopSquare | private int | mDoubleTapSlopSquare | private int | mMinimumFlingVelocity | private static final int | LONGPRESS_TIMEOUT | private static final int | TAP_TIMEOUT | private static final int | DOUBLE_TAP_TIMEOUT | private static final int | SHOW_PRESS | private static final int | LONG_PRESS | private static final int | TAP | private final android.os.Handler | mHandler | private final OnGestureListener | mListener | private OnDoubleTapListener | mDoubleTapListener | private boolean | mStillDown | private boolean | mInLongPress | private boolean | mAlwaysInTapRegion | private boolean | mAlwaysInBiggerTapRegion | private MotionEvent | mCurrentDownEvent | private MotionEvent | mPreviousUpEvent | private boolean | mIsDoubleTappingTrue when the user is still touching for the second tap (down, move, and
up events). Can only be true if there is a double tap listener attached. | private float | mLastMotionY | private float | mLastMotionX | private boolean | mIsLongpressEnabled | private VelocityTracker | mVelocityTrackerDetermines speed during touch scrolling |
Constructors Summary |
---|
public GestureDetector(OnGestureListener listener, android.os.Handler handler)Creates a GestureDetector with the supplied listener.
This variant of the constructor should be used from a non-UI thread
(as it allows specifying the Handler).
this(null, listener, handler);
| public GestureDetector(OnGestureListener listener)Creates a GestureDetector with the supplied listener.
You may only use this constructor from a UI thread (this is the usual situation).
this(null, listener, null);
| public GestureDetector(android.content.Context context, OnGestureListener listener)Creates a GestureDetector with the supplied listener.
You may only use this constructor from a UI thread (this is the usual situation).
this(context, listener, null);
| public GestureDetector(android.content.Context context, OnGestureListener listener, android.os.Handler handler)Creates a GestureDetector with the supplied listener.
You may only use this constructor from a UI thread (this is the usual situation).
if (handler != null) {
mHandler = new GestureHandler(handler);
} else {
mHandler = new GestureHandler();
}
mListener = listener;
if (listener instanceof OnDoubleTapListener) {
setOnDoubleTapListener((OnDoubleTapListener) listener);
}
init(context);
|
Methods Summary |
---|
private void | dispatchLongPress()
mHandler.removeMessages(TAP);
mInLongPress = true;
mListener.onLongPress(mCurrentDownEvent);
| private void | init(android.content.Context context)
if (mListener == null) {
throw new NullPointerException("OnGestureListener must not be null");
}
mIsLongpressEnabled = true;
// Fallback to support pre-donuts releases
int touchSlop, doubleTapSlop;
if (context == null) {
//noinspection deprecation
touchSlop = ViewConfiguration.getTouchSlop();
doubleTapSlop = ViewConfiguration.getDoubleTapSlop();
//noinspection deprecation
mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
} else {
final ViewConfiguration configuration = ViewConfiguration.get(context);
touchSlop = configuration.getScaledTouchSlop();
doubleTapSlop = configuration.getScaledDoubleTapSlop();
mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
}
mTouchSlopSquare = touchSlop * touchSlop;
mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
| 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);
| public boolean | isLongpressEnabled()
return mIsLongpressEnabled;
| public boolean | onTouchEvent(MotionEvent ev)Analyzes the given motion event and if applicable triggers the
appropriate callbacks on the {@link OnGestureListener} supplied.
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) {
case MotionEvent.ACTION_DOWN:
if (mDoubleTapListener != null) {
boolean hadTapMessage = mHandler.hasMessages(TAP);
if (hadTapMessage) mHandler.removeMessages(TAP);
if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
// 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
mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
}
}
mLastMotionX = x;
mLastMotionY = y;
mCurrentDownEvent = MotionEvent.obtain(ev);
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) {
break;
}
final float scrollX = mLastMotionX - x;
final float scrollY = mLastMotionY - y;
if (mIsDoubleTapping) {
// Give the move events of the double-tap
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);
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 = false;
}
} else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
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
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mInLongPress) {
mHandler.removeMessages(TAP);
mInLongPress = false;
} else if (mAlwaysInTapRegion) {
handled = mListener.onSingleTapUp(ev);
} else {
// A fling must travel the minimum tap distance
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000);
final float velocityY = velocityTracker.getYVelocity();
final float velocityX = velocityTracker.getXVelocity();
if ((Math.abs(velocityY) > mMinimumFlingVelocity)
|| (Math.abs(velocityX) > mMinimumFlingVelocity)){
handled = mListener.onFling(mCurrentDownEvent, currentUpEvent, velocityX, velocityY);
}
}
mPreviousUpEvent = MotionEvent.obtain(ev);
mVelocityTracker.recycle();
mVelocityTracker = null;
mIsDoubleTapping = false;
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
break;
case MotionEvent.ACTION_CANCEL:
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
mHandler.removeMessages(TAP);
mVelocityTracker.recycle();
mVelocityTracker = null;
mIsDoubleTapping = false;
mStillDown = false;
if (mInLongPress) {
mInLongPress = false;
break;
}
}
return handled;
| public void | setIsLongpressEnabled(boolean isLongpressEnabled)Set whether longpress is enabled, if this is enabled when a user
presses and holds down you get a longpress event and nothing further.
If it's disabled the user can press and hold down and then later
moved their finger and you will get scroll events. By default
longpress is enabled.
mIsLongpressEnabled = isLongpressEnabled;
| public void | setOnDoubleTapListener(android.view.GestureDetector$OnDoubleTapListener onDoubleTapListener)Sets the listener which will be called for double-tap and related
gestures.
mDoubleTapListener = onDoubleTapListener;
|
|