Android GestureDetector

用于监听手势信息的辅助类,可创建其实例,在View的onTouch方法处调用此类的onTouch方法拦截事件,源码比较简单,直接看源码。

public boolean onTouchEvent(MotionEvent ev) {

    if (mInputEventConsistencyVerifier != null) {

        mInputEventConsistencyVerifier.onTouchEvent(ev, 0);//View中都会调用的方法,用来处理污染,防止多次报告相同的问题,也就是先做过滤处理。

    }

    final int action = ev.getAction();

    if (mCurrentMotionEvent != null) {

        mCurrentMotionEvent.recycle(); //回收上次事件

    }

    mCurrentMotionEvent = MotionEvent.obtain(ev);  //保存本次事件

    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;

    final boolean isGeneratedGesture =

            (ev.getFlags() & MotionEvent.FLAG_IS_GENERATED_GESTURE) != 0;

    // 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: //单手Down

            mDownFocusX = mLastFocusX = focusX; // 记录初始位置

            mDownFocusY = mLastFocusY = focusY;

            // Cancel long press and taps

            cancelTaps();//移除手势Handler中之前保存的状态

            break;

        case MotionEvent.ACTION_POINTER_UP:  // 单手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); // 在300ms之前是否已经发送过TAP消息,此处用来判断是否产生双击事件

                if (hadTapMessage) mHandler.removeMessages(TAP);//表示之前已经Down过,然后移除发送的TAP,也就是不在Handler中进行单机事件回调

                if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null)

                        && hadTapMessage

                        && isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {

                    // This is a second tap

                    mIsDoubleTapping = true;

                    recordGestureClassification(

                            TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP);

                    // 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); // 单击,发送给Handler TAP记录此次事件,消息延迟为300ms

                }

            }

            mDownFocusX = mLastFocusX = focusX;

            mDownFocusY = mLastFocusY = focusY;

            if (mCurrentDownEvent != null) {

                mCurrentDownEvent.recycle(); //回收事件,

            }

            mCurrentDownEvent = MotionEvent.obtain(ev); //创建一个新的 MotionEvent,从现有的复制,native实现

            mAlwaysInTapRegion = true;

            mAlwaysInBiggerTapRegion = true;

            mStillDown = true;

            mInLongPress = false;

            mDeferConfirmSingleTap = false;

            mHasRecordedClassification = false;

            if (mIsLongpressEnabled) { //判断是否长按Enable

                mHandler.removeMessages(LONG_PRESS);

                mHandler.sendMessageAtTime( // 长按一定时间才发送 长按消息

                        mHandler.obtainMessage(

                                LONG_PRESS,

                                TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS,

                                0 /* arg2 */),

                        mCurrentDownEvent.getDownTime()

                                + ViewConfiguration.getLongPressTimeout());

            }

            mHandler.sendEmptyMessageAtTime(SHOW_PRESS,

                    mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);

            handled |= mListener.onDown(ev);  // Down事件最后消耗

            break;

        case MotionEvent.ACTION_MOVE:

            if (mInLongPress || mInContextClick) {

                break;

            }

            // 这个case用来处理滚动事件

            final int motionClassification = ev.getClassification();

            final boolean hasPendingLongPress = mHandler.hasMessages(LONG_PRESS);

            final float scrollX = mLastFocusX - focusX;

            final float scrollY = mLastFocusY - focusY;

            if (mIsDoubleTapping) {

                // Give the move events of the double-tap

                recordGestureClassification(

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

                int slopSquare = isGeneratedGesture ? 0 : mTouchSlopSquare;

                final boolean ambiguousGesture =

                        motionClassification == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE;

                final boolean shouldInhibitDefaultAction =

                        hasPendingLongPress && ambiguousGesture;

                if (shouldInhibitDefaultAction) {

                    // Inhibit default long press

                    if (distance > slopSquare) {

                        // The default action here is to remove long press. But if the touch

                        // slop below gets increased, and we never exceed the modified touch

                        // slop while still receiving AMBIGUOUS_GESTURE, we risk that *nothing*

                        // will happen in response to user input. To prevent this,

                        // reschedule long press with a modified timeout.

                        mHandler.removeMessages(LONG_PRESS);

                        final long longPressTimeout = ViewConfiguration.getLongPressTimeout();

                        mHandler.sendMessageAtTime(

                                mHandler.obtainMessage(

                                        LONG_PRESS,

                                        TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS,

                                        0 /* arg2 */),

                                ev.getDownTime()

                                    + (long) (longPressTimeout * mAmbiguousGestureMultiplier));

                    }

                    // Inhibit default scroll. If a gesture is ambiguous, we prevent scroll

                    // until the gesture is resolved.

                    // However, for safety, simply increase the touch slop in case the

                    // classification is erroneous. Since the value is squared, multiply twice.

                    slopSquare *= mAmbiguousGestureMultiplier * mAmbiguousGestureMultiplier;

                }

                if (distance > slopSquare) {

                    recordGestureClassification(

                            TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL);

                    handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);

                    mLastFocusX = focusX;

                    mLastFocusY = focusY;

                    mAlwaysInTapRegion = false;

                    mHandler.removeMessages(TAP);

                    mHandler.removeMessages(SHOW_PRESS);

                    mHandler.removeMessages(LONG_PRESS);

                }

                int doubleTapSlopSquare = isGeneratedGesture ? 0 : mDoubleTapTouchSlopSquare;

                if (distance > doubleTapSlopSquare) {

                    mAlwaysInBiggerTapRegion = false;

                }

            else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {

                recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL);

                handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);

                mLastFocusX = focusX;

                mLastFocusY = focusY;

            }

            final boolean deepPress =

                    motionClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS;

            if (deepPress && hasPendingLongPress) {

                mHandler.removeMessages(LONG_PRESS);

                mHandler.sendMessage(

                        mHandler.obtainMessage(

                              LONG_PRESS,

                              TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS,

                              0 /* arg2 */));

            }

            break;

        case MotionEvent.ACTION_UP:

            mStillDown = false;

            MotionEvent currentUpEvent = MotionEvent.obtain(ev);

            if (mIsDoubleTapping) {

                // Finally, give the up event of the double-tap

                recordGestureClassification(

                        TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP);

                handled |= mDoubleTapListener.onDoubleTapEvent(ev);

            else if (mInLongPress) {

                mHandler.removeMessages(TAP);

                mInLongPress = false;

            else if (mAlwaysInTapRegion && !mIgnoreNextUpEvent) {

                recordGestureClassification(

                        TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);

                handled = mListener.onSingleTapUp(ev); // 回调单击 down->up

                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;

}

内部处理点击事件的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); //回调ShowPress事件

                break;

            case LONG_PRESS:

                recordGestureClassification(msg.arg1); //回调长按事件

                dispatchLongPress();

                break;

            case TAP:

                // If the user's finger is still down, do not count it as a tap

                if (mDoubleTapListener != null) {

                    if (!mStillDown) {

                        recordGestureClassification(

                                TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);                                         

                        mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent); //回调严格意义上的down事件,注意哈,没有up

                    else {

                        mDeferConfirmSingleTap = true;

                    }

                }

                break;

            default:

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

        }

    }

}

上一篇:整车控制单元(VCU)


下一篇:web移动端基础事件总结与应用