FileDocCategorySizeDatePackage
GestureDetector.javaAPI DocAndroid 1.5 API21398Wed May 06 22:41:56 BST 2009android.view

GestureDetector

public 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
mIsDoubleTapping
True 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
mVelocityTracker
Determines 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).

param
listener the listener invoked for all the callbacks, this must not be null.
param
handler the handler to use
throws
NullPointerException if either {@code listener} or {@code handler} is null.
deprecated
Use {@link #GestureDetector(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead.

        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).

see
android.os.Handler#Handler()
param
listener the listener invoked for all the callbacks, this must not be null.
throws
NullPointerException if {@code listener} is null.
deprecated
Use {@link #GestureDetector(android.content.Context, android.view.GestureDetector.OnGestureListener)} instead.

        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).

see
android.os.Handler#Handler()
param
context the application's context
param
listener the listener invoked for all the callbacks, this must not be null.
throws
NullPointerException if {@code listener} is null.

        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).

see
android.os.Handler#Handler()
param
context the application's context
param
listener the listener invoked for all the callbacks, this must not be null.
param
handler the handler to use
throws
NullPointerException if {@code listener} is null.

        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 voiddispatchLongPress()

        mHandler.removeMessages(TAP);
        mInLongPress = true;
        mListener.onLongPress(mCurrentDownEvent);
    
private voidinit(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 booleanisConsideredDoubleTap(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 booleanisLongpressEnabled()

return
true if longpress is enabled, else false.

        return mIsLongpressEnabled;
    
public booleanonTouchEvent(MotionEvent ev)
Analyzes the given motion event and if applicable triggers the appropriate callbacks on the {@link OnGestureListener} supplied.

param
ev The current motion event.
return
true if the {@link OnGestureListener} consumed the event, else false.

        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 voidsetIsLongpressEnabled(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.

param
isLongpressEnabled whether longpress should be enabled.

        mIsLongpressEnabled = isLongpressEnabled;
    
public voidsetOnDoubleTapListener(android.view.GestureDetector$OnDoubleTapListener onDoubleTapListener)
Sets the listener which will be called for double-tap and related gestures.

param
onDoubleTapListener the listener invoked for all the callbacks, or null to stop listening for double-tap gestures.

        mDoubleTapListener = onDoubleTapListener;