您的位置:首页 > 移动开发 > Android开发

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

2014-10-20 18:07 706 查看
对于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,这样就大功告成了,是不是比以前用的好多了呢?~!.~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: