首页 > 代码库 > 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源码,判断时间间隔和范围)