GestureDetector详解

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都会执行;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值