/// <summary> /// Analyzes the given motion event and if applicable triggers the /// appropriate callbacks on the /// <see cref="OnGestureListener">OnGestureListener</see> /// supplied. /// </summary> /// <param name="ev">The current motion event.</param> /// <returns> /// true if the /// <see cref="OnGestureListener">OnGestureListener</see> /// consumed the event, /// else false. /// </returns> public virtual bool onTouchEvent(android.view.MotionEvent ev) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 0); } int action = ev.getAction(); float y = ev.getY(); float x = ev.getX(); if (mVelocityTracker == null) { mVelocityTracker = android.view.VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); bool handled = false; switch (action & android.view.MotionEvent.ACTION_MASK) { case android.view.MotionEvent.ACTION_POINTER_DOWN: { if (mIgnoreMultitouch) { // Multitouch event - abort. cancel(); } break; } case android.view.MotionEvent.ACTION_POINTER_UP: { // Ending a multitouch gesture and going back to 1 finger if (mIgnoreMultitouch && ev.getPointerCount() == 2) { int index = (((action & android.view.MotionEvent.ACTION_POINTER_INDEX_MASK) >> android.view.MotionEvent .ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0; mLastMotionX = ev.getX(index); mLastMotionY = ev.getY(index); mVelocityTracker.recycle(); mVelocityTracker = android.view.VelocityTracker.obtain(); } break; } case android.view.MotionEvent.ACTION_DOWN: { if (mDoubleTapListener != null) { bool 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); } } mLastMotionX = x; mLastMotionY = y; if (mCurrentDownEvent != null) { mCurrentDownEvent.recycle(); } mCurrentDownEvent = android.view.MotionEvent.obtain(ev); mAlwaysInTapRegion = true; mAlwaysInBiggerTapRegion = true; mStillDown = true; mInLongPress = false; if (mIsLongpressEnabled) { mHandler.removeMessages(LONG_PRESS); mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT + LONGPRESS_TIMEOUT); } mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT ); handled |= mListener.onDown(ev); break; } case android.view.MotionEvent.ACTION_MOVE: { if (mInLongPress || (mIgnoreMultitouch && ev.getPointerCount() > 1)) { break; } float scrollX = mLastMotionX - x; float scrollY = mLastMotionY - y; if (mIsDoubleTapping) { // Give the move events of the double-tap handled |= mDoubleTapListener.onDoubleTapEvent(ev); } else { if (mAlwaysInTapRegion) { int deltaX = (int)(x - mCurrentDownEvent.getX()); int deltaY = (int)(y - mCurrentDownEvent.getY()); int distance = (deltaX * deltaX) + (deltaY * deltaY); if (distance > mTouchSlopSquare) { handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); mLastMotionX = x; mLastMotionY = y; mAlwaysInTapRegion = false; mHandler.removeMessages(TAP); mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(LONG_PRESS); } if (distance > mBiggerTouchSlopSquare) { mAlwaysInBiggerTapRegion = false; } } else { if ((System.Math.Abs(scrollX) >= 1) || (System.Math.Abs(scrollY) >= 1)) { handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); mLastMotionX = x; mLastMotionY = y; } } } break; } case android.view.MotionEvent.ACTION_UP: { mStillDown = false; android.view.MotionEvent currentUpEvent = android.view.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); } else { // A fling must travel the minimum tap distance android.view.VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); float velocityY = velocityTracker.getYVelocity(); float velocityX = velocityTracker.getXVelocity(); if ((System.Math.Abs(velocityY) > mMinimumFlingVelocity) || (System.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; mVelocityTracker.recycle(); mVelocityTracker = null; mIsDoubleTapping = false; mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(LONG_PRESS); break; } case android.view.MotionEvent.ACTION_CANCEL: { cancel(); break; } } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 0); } return(handled); }