首页 > 代码库 > android判断双击事件(参考android源码,判断时间间隔和范围)

android判断双击事件(参考android源码,判断时间间隔和范围)

对于android的双击事件的判断,官方是已经给出解决办法的,主要是使用下面几个类或者接口:GestureDetector,OnGestureListener,OnDoubleTapListener,GestureDetector.SimpleOnGestureListener

对于它们的介绍以及用法很多了,就不说明了,大家可以参考下面的博客:

http://blog.sina.com.cn/s/blog_77c6324101017hs8.html

需要特殊说明的是OnDoubleTapListener这个接口,GestureDetector有个函数setOnDoubleTapListener来设置OnDoubleTapListener,而不是通过构造函数的方式,但让了通过构造函数的方式也不是不可以,大家可以参考下面的博客:

http://www.2cto.com/kf/201211/165457.html

通过上面的学习,相信大家就会对这个几个类用的很熟练了,但是这个并不是我们这篇博客的重点。

如果你因为某些原因,或者说,就是不想用上面的方法,非要用MotionEvent来判断双击的时间的话,那也木有办法!~这个网上也有很多的博客进行了说明。

但是它们的博客无论什么实现,都只是通过时间进行判断,并且也不会进行范围的判断,试想,如果你很快速的点击屏幕最上面和最下面的两个点,如果按照网络上大部分判断时间的做法,那肯定就是双击时间,但是这显然是不合理的。

那么官方源码是怎么判断的呢?我们一起先来看看,博主参考的是android-17版本的源码。

....
 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;
....
很明显的看出来,它们是通过下面这个方法来判断是否需要分发双击事件的:
isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)
下面的代码就是调用listener的接口来处理双击事件,并且获取处理结果,接着后面就是事件分发机制的事情了,不再本文讨论范围之内。
handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
那么,那三个参数是怎么来的呢?我们一个一个来看。

1.mCurrentDownEvent

<span style="color:#333333;">....
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;
            </span><span style="color:#ff6666;">if (mCurrentDownEvent != null) {
                mCurrentDownEvent.recycle();
            }
            mCurrentDownEvent = MotionEvent.obtain(ev);</span><span style="color:#333333;">
....</span>

红色的字体表明了,是上一次触发DOWN事件的MotionEvent。

2.mPriviousUpEvent

....
case MotionEvent.ACTION_UP:
            mStillDown = false;
            <span style="color:#ff6666;">MotionEvent currentUpEvent = MotionEvent.obtain(ev);</span>
            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);
                if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
                    mDoubleTapListener.onSingleTapConfirmed(ev);
                }
            } else {

                // 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);
                }
            }
            <span style="color:#ff6666;">if (mPreviousUpEvent != null) {
                mPreviousUpEvent.recycle();
            }</span>
            // Hold the event we obtained above - listeners may have changed the original.
            <span style="color:#ff6666;">mPreviousUpEvent = currentUpEvent;</span>
....

可以看出来,是上一次触发UP事件的MotionEvent。

3.ev

....
public boolean onTouchEvent(MotionEvent <span style="color:#ff6666;">ev</span>) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(ev, 0);
        }

        final int action = ev.getAction();
....
就是当前发生的MotionEvent的事件。

上述三个参数都找到了,接下来就是看看isConsideredDoubleTap方法里面做了什么。

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);
    }
方法很简单,先判断了事件,再判断了范围。下面看看相关参数的获取,如下:

final ViewConfiguration configuration = ViewConfiguration.get(context);

private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();

doubleTapSlop = configuration.getScaledDoubleTapSlop();

mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;

经过上述的观察,相信大家已经知道了,andorid自己是怎么判断的了吧?相应的,我们可以模仿来写一写。

首先初始化参数:

ViewConfiguration configuration = ViewConfiguration.get(this);

doubleTapSlop = configuration.getScaledDoubleTapSlop();
mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;

然后获取三个参数:

@Override
	public boolean onTouch(View v, MotionEvent event) {
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
			if(isConsideredDoubleTap(firstDown,firstUp,event)){
				hideOrShowTitleBar(menuRl.getVisibility() != View.GONE);
			}
			if (firstDown != null) {
				firstDown.recycle();
            }
			firstDown = MotionEvent.obtain(event);
			hideOrShowTitleBar(menuRl.getVisibility() != View.GONE);
			break;
		case MotionEvent.ACTION_UP:
			if (firstDown != null) {
				firstUp.recycle();
            }
			firstUp = MotionEvent.obtain(event);
			break;
		}
		return false;
	}

最后进行判断:

/**
	 * 一个方法用来判断双击时间
	 * @param firstDown
	 * @param firstUp
	 * @param secondDown
	 * @return
	 */
	private boolean isConsideredDoubleTap(MotionEvent firstDowns,MotionEvent firstUps,MotionEvent secondDowns) {
		if(firstDowns == null || secondDowns == null){
			return false;
		}
//		System.out.println("secondDowns.getEventTime():"+secondDowns.getEventTime());
//		System.out.println("firstUps.getEventTime():"+firstUps.getEventTime());
        if (secondDowns.getEventTime() - firstUps.getEventTime() > Constans.DOUBLE_TAP_TIMEOUT) {
            return false;
        }
       
        int deltaX = (int) firstDowns.getX() - (int) secondDowns.getX();
        int deltaY = (int) firstDowns.getY() - (int) secondDowns.getY();
//        System.out.println("deltaX:"+deltaX);
//        System.out.println("deltaY:"+deltaY);
//        System.out.println("deltaX * deltaX + deltaY * deltaY:"+deltaY);
//        System.out.println("mDoubleTapSlopSquare:"+mDoubleTapSlopSquare);
        return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);
    }

ok,这样就大功告成了,是不是比以前用的好多了呢?~!.~




android判断双击事件(参考android源码,判断时间间隔和范围)