GestureDetector一部分源码分析

GestureDetector部分源码分析

GestureDetector是android给我们提供的用户手势识别类。

GestureDetector关注三个接口OnGestureListener,OnDoubleTapListener ,OnContextClickListener(这个我们先不考虑这个应该是和鼠标之类的外接设备相关的)一个内部类SimpleOnGestureListener,以及两个函数GestureDetector构造函数和GestureDetector的onTouchEvent函数。

OnGestureListener接口管理的是一次触摸屏幕从MotionEvent.ACTION_DOWN -> … ->MotionEvent.ACTION_UP过程产生的事件回调接口。

...
public interface OnGestureListener {

// 手指按下的时候调用
boolean onDown(MotionEvent e);

// 短按(默认100毫秒)
void onShowPress(MotionEvent e);

// 单击手指抬起(MotionEvent.ACTION_UP)的时候触发
boolean onSingleTapUp(MotionEvent e);


/**
 * 手指滑动的时候调用
 * e1: MotionEvent.ACTION_DOWN对应的MotionEvent
 * e2: 当前MotionEvent.ACTION_MOVE对应的MotionEvent
 * distanceX: 每次scroll x方向变化的距离
 * distanceY: 每次scroll y方向变化的距离
 */
boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);

// 长按(100+500=600毫秒)注:一般响应了onLongPress那么onShowPress也会响应的
void onLongPress(MotionEvent e);

/**
 * 在手指抬起的时候如果滑动的速度不管是x方向还是y方向超过了某个值       (50dip/s)就会触发
 * e1: MotionEvent.ACTION_DOWN对应的MotionEvent
 * e2: MotionEvent.ACTION_UP对应的MotionEvent
 * velocityX: x方向速度
 * velocityY: y方向速度
 */
boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
}

...

OnDoubleTapListener接口管理的是满足双击条件产生的事件。

    ...
    public interface OnDoubleTapListener {

        // 单击事件调用,只有确认了不是双击事件,不是长按事件,不是滑动事件 该函数调用
        boolean onSingleTapConfirmed(MotionEvent e);

        // 满足双击事件,在第二次单击MotionEvent.ACTION_DOWN的时候触发这个事件
        boolean onDoubleTap(MotionEvent e);

        // 满足双击事件,第二次单击过程中的产生的MotionEvent回调(第二次轻触过程MotionEvent.ACTION_DOWN -> ... ->MotionEvent.ACTION_UP一系列事件)
        boolean onDoubleTapEvent(MotionEvent e);

    }
    ...

OnContextClickListener 和鼠标之类的外接设备相关。

SimpleOnGestureListener 内部类实现了OnGestureListener,OnDoubleTapListener ,OnContextClickListener三个接口的class 这样做的好处是 在使用SimpleOnGestureListener的时候只需要重写我们关心的回调方法就好了。不用重写所有的方法。

接下来按照GestureDetector的使用流程来分析,一般使用GestureDetector的步骤都是先构造出GestureDetector对象,然后就是在我们要监听的View设置setOnTouchListener在onTouch里面调用GestureDetector的onTouchEvent函数。

GestureDetector构造函数

    ...

    public GestureDetector(OnGestureListener listener, Handler handler) {
        this(null, listener, handler);
    }

    public GestureDetector(OnGestureListener listener) {
        this(null, listener, null);
    }

    public GestureDetector(Context context, OnGestureListener listener) {
        this(context, listener, null);
    }

    public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
        if (handler != null) {
            mHandler = new GestureHandler(handler);
        } else {
            mHandler = new GestureHandler();
        }
        mListener = listener;
        if (listener instanceof OnDoubleTapListener) {
            setOnDoubleTapListener((OnDoubleTapListener) listener);
        }
        if (listener instanceof OnContextClickListener) {
            setContextClickListener((OnContextClickListener) listener);
        }
        init(context);
    }

    public GestureDetector(Context context, OnGestureListener listener, Handler handler,
                           boolean unused) {
        this(context, listener, handler);
    }

    ...

不管调用的哪个构造函数最后都是走的一个,

    public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
        if (handler != null) {
            mHandler = new GestureHandler(handler);
        } else {
            mHandler = new GestureHandler();
        }
        mListener = listener;
        if (listener instanceof OnDoubleTapListener) {
            setOnDoubleTapListener((OnDoubleTapListener) listener);
        }
        if (listener instanceof OnContextClickListener) {
            setContextClickListener((OnContextClickListener) listener);
        }
        init(context);
    }

三个参数,context没什么说的,listener 设置回调这里注意如果这里传进来的是SimpleOnGestureListener对象那么两个instanceof都是会走的setOnDoubleTapListener函数和setContextClickListener函数都会调用到,handler GestureHandler对象,因为SHOW_PRESS,LONG_PRESS,TAP三个对应的事件都是要靠一定的延时才能判断是否发生所以借助handler 的 sendEmptyMessageDelayed,可以看到构造函数就干了三件事,一个是handler,一个是设置listner 也就是前面说的三个interface,最后一个就是init(context)。接着就应该瞧下handler和init(context)。

GestureHandler 代码如下

    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) {
                        if (!mStillDown) {
                            mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
                        } else {
                            mDeferConfirmSingleTap = true;
                        }
                    }
                    break;

                default:
                    throw new RuntimeException("Unknown message " + msg); //never
            }
        }
    }

handleMessage 里面三种消息SHOW_PRESS(短按对应的事件), LONG_PRESS(长按对应的事件), TAP(确认单击事件)。为什么会有这个GestureHandler。因为SHOW_PRESS,LONG_PRESS,TAP三个对应的事件都是要靠一定的延时才能判断是否发生,不能在每个MotionEvent的时候决定是否发生。
看下case TAP:的情况。mStillDown如果不是按下的状态(手指已经离开了),并且TAP还在队列里面。则调用onSingleTapConfirmed。如果还是按下的状态并且TAP还在消息队列里面则推迟判断(会在MotionEvent.ACTION_UP的时候做进一步的处理)。

init函数 做一些初始化的操作,初始化一些变量用做判断的数据。

    private void init(Context context) {
        if (mListener == null) {
            throw new NullPointerException("OnGestureListener must not be null");
        }
        mIsLongpressEnabled = true;

        // Fallback to support pre-donuts releases
        int touchSlop, doubleTapSlop, doubleTapTouchSlop;
        if (context == null) {
            //noinspection deprecation
            touchSlop = ViewConfiguration.getTouchSlop();
            doubleTapTouchSlop = touchSlop; // Hack rather than adding a hiden method for this
            doubleTapSlop = ViewConfiguration.getDoubleTapSlop();
            //noinspection deprecation
            mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
            mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity();
        } else {
            final ViewConfiguration configuration = ViewConfiguration.get(context);
            touchSlop = configuration.getScaledTouchSlop();
            doubleTapTouchSlop = configuration.getScaledDoubleTapTouchSlop();
            doubleTapSlop = configuration.getScaledDoubleTapSlop();
            mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
            mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();
        }
        mTouchSlopSquare = touchSlop * touchSlop;
        mDoubleTapTouchSlopSquare = doubleTapTouchSlop * doubleTapTouchSlop;
        mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
    }

如果mListener为null对不起异常了。
mIsLongpressEnabled: 是否支持长按操作。
mMinimumFlingVelocity(50dip/s): fling判断移动的最小速度。
mMaximumFlingVelocity(8000dip/s): fling判断移动的最大速度;
mTouchSlopSquare(8 * 8): 用来判断是否开始scroll。
mDoubleTapTouchSlopSquare(8 * 8): 判断双击的时候用到,第一个单击的时候产生了MotionEvent.ACTION_MOVE,并且move的距离超过了这个值 就不认为是双击事件。
mDoubleTapSlopSquare(100 * 100): 判断双击的时候用到,两次单击范围要在这个值之内。否则不算是双击事件。

接下来最重要的函数onTouchEvent 我们就关心三个action分别是MotionEvent.ACTION_DOWN,MotionEvent.ACTION_MOVE,MotionEvent.ACTION_UP

    public boolean onTouchEvent(MotionEvent ev) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(ev, 0);
        }

        final int action = ev.getAction();

        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(ev);

        final boolean pointerUp =
            (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
        final int skipIndex = pointerUp ? ev.getActionIndex() : -1;

        // Determine focal point
        float sumX = 0, sumY = 0;
        final int count = ev.getPointerCount();
        for (int i = 0; i < count; i++) {
            if (skipIndex == i) continue;
            sumX += ev.getX(i);
            sumY += ev.getY(i);
        }
        final int div = pointerUp ? count - 1 : count;
        final float focusX = sumX / div;
        final float focusY = sumY / div;

        boolean handled = false;

        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_POINTER_DOWN:
                mDownFocusX = mLastFocusX = focusX;
                mDownFocusY = mLastFocusY = focusY;
                // Cancel long press and taps
                cancelTaps();
                break;

            case MotionEvent.ACTION_POINTER_UP:
                mDownFocusX = mLastFocusX = focusX;
                mDownFocusY = mLastFocusY = focusY;

                // Check the dot product of current velocities.
                // If the pointer that left was opposing another velocity vector, clear.
                mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
                final int upIndex = ev.getActionIndex();
                final int id1 = ev.getPointerId(upIndex);
                final float x1 = mVelocityTracker.getXVelocity(id1);
                final float y1 = mVelocityTracker.getYVelocity(id1);
                for (int i = 0; i < count; i++) {
                    if (i == upIndex) continue;

                    final int id2 = ev.getPointerId(i);
                    final float x = x1 * mVelocityTracker.getXVelocity(id2);
                    final float y = y1 * mVelocityTracker.getYVelocity(id2);

                    final float dot = x + y;
                    if (dot < 0) {
                        mVelocityTracker.clear();
                        break;
                    }
                }
                break;

            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);
                    }
                }

                mDownFocusX = mLastFocusX = focusX;
                mDownFocusY = mLastFocusY = focusY;
                if (mCurrentDownEvent != null) {
                    mCurrentDownEvent.recycle();
                }
                mCurrentDownEvent = MotionEvent.obtain(ev);
                mAlwaysInTapRegion = true;
                mAlwaysInBiggerTapRegion = true;
                mStillDown = true;
                mInLongPress = false;
                mDeferConfirmSingleTap = 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 || mInContextClick) {
                    break;
                }
                final float scrollX = mLastFocusX - focusX;
                final float scrollY = mLastFocusY - focusY;
                if (mIsDoubleTapping) {
                    // Give the move events of the double-tap
                    handled |= mDoubleTapListener.onDoubleTapEvent(ev);
                } else if (mAlwaysInTapRegion) {
                    final int deltaX = (int) (focusX - mDownFocusX);
                    final int deltaY = (int) (focusY - mDownFocusY);
                    int distance = (deltaX * deltaX) + (deltaY * deltaY);
                    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);
                    }
                    if (distance > mDoubleTapTouchSlopSquare) {
                        mAlwaysInBiggerTapRegion = false;
                    }
                } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
                    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) {
                    // Finally, give the up event of the double-tap
                    handled |= mDoubleTapListener.onDoubleTapEvent(ev);
                } else if (mInLongPress) {
                    mHandler.removeMessages(TAP);
                    mInLongPress = false;
                } else if (mAlwaysInTapRegion && !mIgnoreNextUpEvent) {
                    handled = mListener.onSingleTapUp(ev);
                    if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
                        mDoubleTapListener.onSingleTapConfirmed(ev);
                    }
                } else if (!mIgnoreNextUpEvent) {

                    // A fling must travel the minimum tap distance
                    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;
                mIgnoreNextUpEvent = 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;
    }

首先是case MotionEvent.ACTION_DOWN动作的处理过程。
直接跳到第65行,case MotionEvent.ACTION_DOWN:部分 if mDoubleTapListener 不等于null(如果设置了OnDoubleTapListener)这个好理解在构造函数的传入了SimpleOnGestureListener的对象或者调用了setOnDoubleTapListener()函数,总的来说case MotionEvent.ACTION_DOWN做了三件事情
1. 判断队列里面是否有TAP message,如果有则remove掉,TAP的延时判断时间是DOUBLE_TAP_TIMEOUT(300毫秒) 换句话就是如果在前一次单击到下一次单击的时间在300毫秒之内前一次的TAP舍弃掉。
2. 判断是否满足双击条件。两种情况满足和不满足。
如果满足:mIsDoubleTapping = ture记录当前是双击,并且调用onDoubleTap onDoubleTapEvent两个回调函数。
如果不满足:说明是一个全新的按键事件了和前一次按键没关系了 准备判断TAP。
mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT); send TAP message delay 时间 DOUBLE_TAP_TIMEOUT(300毫秒)

继续看下双击事件的判断条件if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage && isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev))四个条件
1). mCurrentDownEvent表示上一次的ACTION_DOWN的MotionEvent事件(注意这里还没有赋值成当前的MotionEvent)。保证有第一次的点击。
2). mPreviousUpEvent表示上一次的MotionEvent.ACTION_UP的MotionEvent事件。保证有第一次的点击。
3). hadTapMessage 队列里面有TAP message 说明两次单击的时间间隔在300毫秒之内。
4). isConsideredDoubleTap() 通过判断第一次单击down动作到up动作是不是满足双击的条件(时间间隔, 距离)的。
isConsideredDoubleTap 两个参数第一次单击时候的down对应的MotionEvent 第一次单击时候up对应的MotionEvent事件。

    private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
                                          MotionEvent secondDown) {
        if (!mAlwaysInBiggerTapRegion) {
            return false;
        }

        final long deltaTime = secondDown.getEventTime() - firstUp.getEventTime();
        if (deltaTime > DOUBLE_TAP_TIMEOUT || deltaTime < DOUBLE_TAP_MIN_TIME) {
            return false;
        }

        int deltaX = (int) firstDown.getX() - (int) secondDown.getX();
        int deltaY = (int) firstDown.getY() - (int) secondDown.getY();
        return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);
    }

第3行:mAlwaysInBiggerTapRegion表示在第一次单击的过程中产生了ACTION_MOVE事件,并且move的距离超过了一定范围(这个范围就是init函数中说的mDoubleTapTouchSlopSquare) mAlwaysInBiggerTapRegion赋值的地方在case MotionEvent.ACTION_MOVE过程里面处理。
第7行:通过第一次单击从down到up的过程的时间来判断是不是属于双击事件(40~300毫秒之内)。
第13行:通过第一次单击从down和up两个动作对应的MotionEvent是否在一定的范围之内(这个范围就是init函数中说的mDoubleTapSlopSquare)

总结下双击事件产生的条件 四个条件。
1. 两次单击的时间间隔在300毫秒之内
2. 第一次单击如果产生了ACTION_MOVE事件,则ACTION_MOVE move的距离不能超过mDoubleTapTouchSlopSquare(默认是8)
3. 第一次单击down到up的过程事件在DOUBLE_TAP_MIN_TIME ~ DOUBLE_TAP_TIMEOUT(40毫秒~300毫秒)之间
4. 第一次单击down和up对应的MotionEvent距离 不能超过mDoubleTapSlopSquare(100dip)

接着onTouchEvent第95行 mIsLongpressEnabled如果支持长按事件 准备LONG_PRESS判断。 mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT + LONGPRESS_TIMEOUT); send LONG_PRESS message delay TAP_TIMEOUT + LONGPRESS_TIMEOUT(100 + 500 = 600毫秒)
第100行 准备SHOW_PRESS判断,mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT); send SHOW_PRESS message delay TAP_TIMEOUT(100毫秒)
第101行 调用onDown的call back 可以看到每次down的动作都会调用该函数。

其次是case MotionEvent.ACTION_MOVE动作的处理过程。
第104行 case MotionEvent.ACTION_MOVE 对ACTION_MOVE 事件的处理。
第105行 如果当前产生了long click事件 或者 content click事件 直接break ,产生了这两个事件就不让产生onScroll事件,也就是说不让回调onScroll函数。
第110行 if (mIsDoubleTapping) 如果当前是双击事件 则回调OnDoubleTapListener.onDoubleTapEvent()
:OnDoubleTapListener.onDoubleTapEvent() 的调用是在第二次单击从按下到抬起过程中产生的触摸事件的回调,这个函数会多次调用(至少得有ACTION_DOWN和ACTION_UP对应的MotionEvent事件 )。
第113~128行 mAlwaysInTapRegion 表示当前触点和ACTION_DOWN时候的触点相比是否还在单击的范围之内(<8dip) 接下来就是具体的判断如果大于mTouchSlopSquare 则回调OnGestureListener.onScroll()。同时把TAP,SHOW_PRESS,LONG_PRESS从队列中move掉。可以看出如果产生了OnGestureListener.onScroll()就不会产生OnGestureListener.onShowPress(), OnGestureListener.onLongPress(), OnDoubleTapListener.onSingleTapConfirmed()对应的回调了。
同时判断distance > mDoubleTapTouchSlopSquare 为双击的判断做好准备。前面有提到过双击判断的条件有一点是 第一次单击如果产生了ACTION_MOVE事件,则ACTION_MOVE move的距离不能超过mDoubleTapTouchSlopSquare(默认是8) 就是在这里设置的。
第129~133行 两个触点距离大于1dip则回调OnGestureListener.onScroll() 方法。

最后是case MotionEvent.ACTION_UP 动作的处理过程。
第136行:MotionEvent.ACTION_UP 事件处理。一堆if的判断,如果是双击事件则是执行OnDoubleTapListener.onDoubleTapEvent(),如处理了long click 则把TAP从消息队里里面move掉。如果是单击事件回调OnGestureListener.onSingleTapUp(),同时判断是否要在ACTION_UP的时候回调OnDoubleTapListener.onSingleTapConfirmed()。最后一个if通过x或者y方向的移动速度判断是否满足OnGestureListener.onFling()的回调。
最后把SHOW_PRESS,LONG_PRESS从消息队列里面移除掉。

整个onTouchEvent就结束了。

总结下onTouchEvent对MotionEvent.ACTION_DOWN,MotionEvent.ACTION_MOVE,MotionEvent.ACTION_UP三个case的处理
1. MotionEvent.ACTION_DOWN:
1). 判断是否满足双击的条件,如果满足回调onDoubleTap() onDoubleTapEvent()两个函数。如果不满足发送TAP延时消息。
2). 记录 mDownFocusX, mDownFocusY, mCurrentDownEvent 变量的值。
3). 发送SHOW_PRESS,LONG_PRESS 延时消息。
4). 回调onDown 每次都会调用

  1. MotionEvent.ACTION_MOVE
    1). 如果已经处理了long click 事件或者 context click事件。直接break。
    2). 判断当前是不是双击的情况 如果是直接调用onDoubleTapEvent()函数。
    3). 判断move的值是否大于8dip如果大于准备调用onScroll() 同时remoe掉TAP,SHOW_PRESS,LONG_PRESS消息。

  2. MotionEvent.ACTION_UP:
    1). 判断当前是不是双击的情况 如果是直接调用onDoubleTapEvent()函数。
    2). 判断是不是处理了long click 事件。
    3). 判断是不是还是TAP事件,是就调用onSingleTapUp 同时判断是不是要调用onSingleTapConfirmed。
    4). 判断是不是fling 需不需要调用onFling。
    5). remove 掉 SHOW_PRESS,LONG_PRESS。

通过分析GestureDetector代码总结出一些动作产生的回调函数。如下
1. 单击
onDown -> onSingleTapUp -> onSingleTapConfirmed
2. 短按
onDown -> onShowPress -> onSingleTapUp -> onSingleTapConfirmed
3. 长按
onDown -> onShowPress -> onLongPress
4. 滑动
onDown -> onScroll -> onFling(这个可能有也可能没有决定权在滑动的速度)
5. 双击
onDown -> onSingleTapUp -> onDoubleTap - > onDoubleTapEvent(down) -> onDown -> onDoubleTapEvent(up)
6. 双击但是第二次晚点抬起手指。
onDown -> onSingleTapUp -> onDoubleTap - > onDoubleTapEvent(down) -> onDown -> onDoubleTapEvent(move)不止一个 -> onShowPress -> onDoubleTapEvent(move)不止一个 -> onLongPress -> onDoubleTapEvent(up)

在提点额外的,如果在使用GestureDetector的过程中,onDown的call back中没有return true,而是return false。会出现什么结果呢。通过上面的分析onDown的call back是在onTouchEvent 的MotionEvent.ACTION_DOWN中回调的,如果我们在MotionEvent.ACTION_DOWN的时候返回false那么 MotionEvent.ACTION_MOVE,MotionEvent.ACTION_UP不会传过来的。
所以如果我们在onDown的call back中return false。不管什么触摸动作回调顺序都是onDown -> onShowPress -> onLongPress。