編輯:關於Android編程
對於android的雙擊事件的判斷,官方是已經給出解決辦法的,主要是使用下面幾個類或者接口:GestureDetector,OnGestureListener,OnDoubleTapListener,GestureDetector.SimpleOnGestureListener
對於它們的介紹以及用法很多了,就不說明了,大家可以參考下面的博客:
http://blog.sina.com.cn/s/blog_77c6324101017hs8.html
需要特殊說明的是OnDoubleTapListener這個接口,GestureDetector有個函數setOnDoubleTapListener來設置OnDoubleTapListener,而不是通過構造函數的方式,但讓了通過構造函數的方式也不是不可以,大家可以參考下面的博客:
/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
.... 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); ....
2.mPriviousUpEvent
.... case MotionEvent.ACTION_UP: mStillDown = false; MotionEvent currentUpEvent = MotionEvent.obtain(ev); 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); } } if (mPreviousUpEvent != null) { mPreviousUpEvent.recycle(); } // Hold the event we obtained above - listeners may have changed the original. mPreviousUpEvent = currentUpEvent; ....
3.ev
.... public boolean onTouchEvent(MotionEvent ev) { 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); }
一直使用Eclipse環境開發Android,也嘗鮮使用過Android Studio去開發,各種IDE配合Android SDK及SDK原生的Android Emula
一個Activity掌握Android4.0新控件谷歌在推出Android4.0的同時推出了一些新控件,Android4.0中最常用的新控件有下面5種。1. Switch
本文實例講述了Android開發中MotionEvent坐標獲取方法。分享給大家供大家參考,具體如下:Android MotionEvent中getX()與getRawX
先看個簡單的,先上個效果圖,吸引大家一下眼球。三個頁面間的滑動,此時是帶著上面的標題一塊滑動的。看一下android 對於PagerTitleStrip的官方解釋:Pag