public override bool dispatchTouchEvent(android.view.MotionEvent ev) { if (mDefaultTouchRecepient == null) { return base.dispatchTouchEvent(ev); } if (base.dispatchTouchEvent(ev)) { return true; } mTempRect.set(0, 0, 0, 0); offsetRectIntoDescendantCoords(mDefaultTouchRecepient, mTempRect); ev.setLocation(ev.getX() + mTempRect.left, ev.getY() + mTempRect.top); return mDefaultTouchRecepient.dispatchTouchEvent(ev); }
private void onSecondaryPointerUp(android.view.MotionEvent ev) { int pointerIndex = (ev.getAction() & android.view.MotionEvent.ACTION_POINTER_INDEX_MASK ) >> android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT; int pointerId = ev.getPointerId(pointerIndex); if (pointerId == mActivePointerId) { // This was our active pointer going up. Choose a new // active pointer and adjust accordingly. // TODO: Make this decision more intelligent. int newPointerIndex = pointerIndex == 0 ? 1 : 0; mLastMotionX = ev.getX(newPointerIndex); mActivePointerId = ev.getPointerId(newPointerIndex); if (mVelocityTracker != null) { mVelocityTracker.clear(); } } }
private bool isConsideredDoubleTap(android.view.MotionEvent firstDown, android.view.MotionEvent firstUp, android.view.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); }
public override bool onTouchEvent(android.view.MotionEvent @event) { if (!mIsUserSeekable || !isEnabled()) { return false; } switch (@event.getAction()) { case android.view.MotionEvent.ACTION_DOWN: { if (isInScrollingContainer()) { mTouchDownX = @event.getX(); } else { setPressed(true); if (mThumb != null) { invalidate(mThumb.getBounds()); } // This may be within the padding region onStartTrackingTouch(); trackTouchEvent(@event); attemptClaimDrag(); } break; } case android.view.MotionEvent.ACTION_MOVE: { if (mIsDragging) { trackTouchEvent(@event); } else { float x = @event.getX(); if (System.Math.Abs(x - mTouchDownX) > mScaledTouchSlop) { setPressed(true); if (mThumb != null) { invalidate(mThumb.getBounds()); } // This may be within the padding region onStartTrackingTouch(); trackTouchEvent(@event); attemptClaimDrag(); } } break; } case android.view.MotionEvent.ACTION_UP: { if (mIsDragging) { trackTouchEvent(@event); onStopTrackingTouch(); setPressed(false); } else { // Touch up when we never crossed the touch slop threshold should // be interpreted as a tap-seek to that location. onStartTrackingTouch(); trackTouchEvent(@event); onStopTrackingTouch(); } // ProgressBar doesn't know to repaint the thumb drawable // in its inactive state when the touch stops (because the // value has not apparently changed) invalidate(); break; } case android.view.MotionEvent.ACTION_CANCEL: { if (mIsDragging) { onStopTrackingTouch(); setPressed(false); } invalidate(); // see above explanation break; } } return true; }
public virtual bool onTouch(android.view.View v, android.view.MotionEvent @event) { int action = @event.getAction(); int x = (int)@event.getX(); int y = (int)@event.getY(); if (action == android.view.MotionEvent.ACTION_DOWN && this._enclosing.mPopup != null && this._enclosing.mPopup.isShowing() && (x >= 0 && x < this._enclosing.mPopup. getWidth() && y >= 0 && y < this._enclosing.mPopup.getHeight())) { this._enclosing.mHandler.postDelayed(this._enclosing.mResizePopupRunnable, android.widget.ListPopupWindow .EXPAND_LIST_TIMEOUT); } else { if (action == android.view.MotionEvent.ACTION_UP) { this._enclosing.mHandler.removeCallbacks(this._enclosing.mResizePopupRunnable); } } return false; }
protected internal override bool dispatchGenericPointerEvent(android.view.MotionEvent @event) { // Send the event to the child under the pointer. int childrenCount = mChildrenCount; if (childrenCount != 0) { android.view.View[] children = mChildren; float x = @event.getX(); float y = @event.getY(); { for (int i = childrenCount - 1; i >= 0; i--) { android.view.View child = children[i]; if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child , null)) { continue; } if (dispatchTransformedGenericPointerEvent(@event, child)) { return true; } } } } // No child handled the event. Send it to this view group. return base.dispatchGenericPointerEvent(@event); }
public virtual bool onDown(android.view.MotionEvent e) { // Kill any existing fling/scroll mFlingRunnable.stop(false); // Get the item's view that was touched mDownTouchPosition = pointToPosition((int)e.getX(), (int)e.getY()); if (mDownTouchPosition >= 0) { mDownTouchView = getChildAt(mDownTouchPosition - mFirstPosition); mDownTouchView.setPressed(true); } // Reset the multiple-scroll tracking state mIsFirstScroll = true; // Must return true to get matching events for this down event. return true; }
/// <summary>MotionEvent has no getRawX(int) method; simulate it pending future API approval. /// </summary> /// <remarks>MotionEvent has no getRawX(int) method; simulate it pending future API approval. /// </remarks> private static float getRawX(android.view.MotionEvent @event, int pointerIndex) { if (pointerIndex < 0) { return float.MinValue; } if (pointerIndex == 0) { return @event.getRawX(); } float offset = @event.getRawX() - @event.getX(); return @event.getX(pointerIndex) + offset; }
public override bool onTouchEvent(android.view.MotionEvent ev) { base.onTouchEvent(ev); int action = ev.getAction(); int pointerIndex = ev.findPointerIndex(mActivePointerId); if (pointerIndex == INVALID_POINTER) { // no data for our primary pointer, this shouldn't happen, log it android.util.Log.d(TAG, "Error: No data for our primary pointer."); return false; } float newY = ev.getY(pointerIndex); float newX = ev.getX(pointerIndex); float deltaY = newY - mInitialY; float deltaX = newX - mInitialX; if (mVelocityTracker == null) { mVelocityTracker = android.view.VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); switch (action & android.view.MotionEvent.ACTION_MASK) { case android.view.MotionEvent.ACTION_MOVE: { beginGestureIfNeeded(deltaY); float rx = deltaX / (mSlideAmount * 1.0f); if (mSwipeGestureType == GESTURE_SLIDE_DOWN) { float r = (deltaY - mTouchSlop * 1.0f) / mSlideAmount * 1.0f; if (mStackMode == ITEMS_SLIDE_DOWN) { r = 1 - r; } mStackSlider.setYProgress(1 - r); mStackSlider.setXProgress(rx); return true; } else { if (mSwipeGestureType == GESTURE_SLIDE_UP) { float r = -(deltaY + mTouchSlop * 1.0f) / mSlideAmount * 1.0f; if (mStackMode == ITEMS_SLIDE_DOWN) { r = 1 - r; } mStackSlider.setYProgress(r); mStackSlider.setXProgress(rx); return true; } } break; } case android.view.MotionEvent.ACTION_UP: { handlePointerUp(ev); break; } case android.view.MotionEvent.ACTION_POINTER_UP: { onSecondaryPointerUp(ev); break; } case android.view.MotionEvent.ACTION_CANCEL: { mActivePointerId = INVALID_POINTER; mSwipeGestureType = GESTURE_NONE; break; } } return true; }
public override bool onInterceptTouchEvent(android.view.MotionEvent ev) { int action = ev.getAction(); switch (action & android.view.MotionEvent.ACTION_MASK) { case android.view.MotionEvent.ACTION_DOWN: { if (mActivePointerId == INVALID_POINTER) { mInitialX = ev.getX(); mInitialY = ev.getY(); mActivePointerId = ev.getPointerId(0); } break; } case android.view.MotionEvent.ACTION_MOVE: { int pointerIndex = ev.findPointerIndex(mActivePointerId); if (pointerIndex == INVALID_POINTER) { // no data for our primary pointer, this shouldn't happen, log it android.util.Log.d(TAG, "Error: No data for our primary pointer."); return false; } float newY = ev.getY(pointerIndex); float deltaY = newY - mInitialY; beginGestureIfNeeded(deltaY); break; } case android.view.MotionEvent.ACTION_POINTER_UP: { onSecondaryPointerUp(ev); break; } case android.view.MotionEvent.ACTION_UP: case android.view.MotionEvent.ACTION_CANCEL: { mActivePointerId = INVALID_POINTER; mSwipeGestureType = GESTURE_NONE; break; } } return mSwipeGestureType != GESTURE_NONE; }
public override bool onInterceptTouchEvent(android.view.MotionEvent ev) { int action = ev.getAction(); if ((action == android.view.MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) { return true; } switch (action & android.view.MotionEvent.ACTION_MASK) { case android.view.MotionEvent.ACTION_MOVE: { int activePointerId = mActivePointerId; if (activePointerId == INVALID_POINTER) { // If we don't have a valid id, the touch down wasn't on content. break; } int pointerIndex = ev.findPointerIndex(activePointerId); float y = ev.getY(pointerIndex); int yDiff = (int)System.Math.Abs(y - mLastMotionY); if (yDiff > mTouchSlop) { mIsBeingDragged = true; mLastMotionY = y; initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); if (mScrollStrictSpan == null) { mScrollStrictSpan = android.os.StrictMode.enterCriticalSpan("ScrollView-scroll"); } } break; } case android.view.MotionEvent.ACTION_DOWN: { float y = ev.getY(); if (!inChild((int)ev.getX(), (int)y)) { mIsBeingDragged = false; recycleVelocityTracker(); break; } mLastMotionY = y; mActivePointerId = ev.getPointerId(0); initOrResetVelocityTracker(); mVelocityTracker.addMovement(ev); mIsBeingDragged = !mScroller.isFinished(); if (mIsBeingDragged && mScrollStrictSpan == null) { mScrollStrictSpan = android.os.StrictMode.enterCriticalSpan("ScrollView-scroll"); } break; } case android.view.MotionEvent.ACTION_CANCEL: case android.view.MotionEvent.ACTION_UP: { mIsBeingDragged = false; mActivePointerId = INVALID_POINTER; recycleVelocityTracker(); if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) { invalidate(); } break; } case android.view.MotionEvent.ACTION_POINTER_UP: { onSecondaryPointerUp(ev); break; } } return mIsBeingDragged; }
internal virtual bool onTouchEvent(android.view.MotionEvent me) { if (mState == STATE_NONE) { return false; } int action = me.getAction(); if (action == android.view.MotionEvent.ACTION_DOWN) { if (isPointInside(me.getX(), me.getY())) { if (!mList.isInScrollingContainer()) { beginDrag(); return true; } mInitialTouchY = me.getY(); startPendingDrag(); } } else { if (action == android.view.MotionEvent.ACTION_UP) { // don't add ACTION_CANCEL here if (mPendingDrag) { // Allow a tap to scroll. beginDrag(); int viewHeight = mList.getHeight(); // Jitter int newThumbY = (int)me.getY() - mThumbH + 10; if (newThumbY < 0) { newThumbY = 0; } else { if (newThumbY + mThumbH > viewHeight) { newThumbY = viewHeight - mThumbH; } } mThumbY = newThumbY; scrollTo((float)mThumbY / (viewHeight - mThumbH)); cancelPendingDrag(); } // Will hit the STATE_DRAGGING check below if (mState == STATE_DRAGGING) { if (mList != null) { // ViewGroup does the right thing already, but there might // be other classes that don't properly reset on touch-up, // so do this explicitly just in case. mList.requestDisallowInterceptTouchEvent(false); mList.reportScrollStateChange(android.widget.AbsListView.OnScrollListenerClass.SCROLL_STATE_IDLE ); } setState(STATE_VISIBLE); android.os.Handler handler = mHandler; handler.removeCallbacks(mScrollFade); if (!mAlwaysShow) { handler.postDelayed(mScrollFade, 1000); } mList.invalidate(); return true; } } else { if (action == android.view.MotionEvent.ACTION_MOVE) { if (mPendingDrag) { float y = me.getY(); if (System.Math.Abs(y - mInitialTouchY) > mScaledTouchSlop) { setState(STATE_DRAGGING); if (mListAdapter == null && mList != null) { getSectionsFromIndexer(); } if (mList != null) { mList.requestDisallowInterceptTouchEvent(true); mList.reportScrollStateChange(android.widget.AbsListView.OnScrollListenerClass.SCROLL_STATE_TOUCH_SCROLL ); } cancelFling(); cancelPendingDrag(); } } // Will hit the STATE_DRAGGING check below if (mState == STATE_DRAGGING) { int viewHeight = mList.getHeight(); // Jitter int newThumbY = (int)me.getY() - mThumbH + 10; if (newThumbY < 0) { newThumbY = 0; } else { if (newThumbY + mThumbH > viewHeight) { newThumbY = viewHeight - mThumbH; } } if (System.Math.Abs(mThumbY - newThumbY) < 2) { return true; } mThumbY = newThumbY; // If the previous scrollTo is still pending if (mScrollCompleted) { scrollTo((float)mThumbY / (viewHeight - mThumbH)); } return true; } } else { if (action == android.view.MotionEvent.ACTION_CANCEL) { cancelPendingDrag(); } } } } return false; }
internal virtual bool onInterceptTouchEvent(android.view.MotionEvent ev) { switch (ev.getActionMasked()) { case android.view.MotionEvent.ACTION_DOWN: { if (mState > STATE_NONE && isPointInside(ev.getX(), ev.getY())) { if (!mList.isInScrollingContainer()) { beginDrag(); return true; } mInitialTouchY = ev.getY(); startPendingDrag(); } break; } case android.view.MotionEvent.ACTION_UP: case android.view.MotionEvent.ACTION_CANCEL: { cancelPendingDrag(); break; } } return false; }
private bool isOutOfBounds(android.content.Context context, android.view.MotionEvent @event) { int x = (int)@event.getX(); int y = (int)@event.getY(); int slop = android.view.ViewConfiguration.get(context).getScaledWindowTouchSlop(); android.view.View decorView = getDecorView(); return (x < -slop) || (y < -slop) || (x > (decorView.getWidth() + slop)) || (y > (decorView.getHeight() + slop)); }
/// <returns>If the <code>event</code> is in the <code>view</code>.</returns> private bool isEventInViewHitRect(android.view.MotionEvent @event, android.view.View view) { view.getHitRect(mTempRect); return mTempRect.contains((int)@event.getX(), (int)@event.getY()); }
public virtual bool onTouchEvent(android.view.MotionEvent @event) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(@event, 0); } int action = @event.getActionMasked(); if (action == android.view.MotionEvent.ACTION_DOWN) { reset(); } // Start fresh bool handled = true; if (mInvalidGesture) { handled = false; } else { if (!mGestureInProgress) { switch (action) { case android.view.MotionEvent.ACTION_DOWN: { mActiveId0 = @event.getPointerId(0); mActive0MostRecent = true; break; } case android.view.MotionEvent.ACTION_UP: { reset(); break; } case android.view.MotionEvent.ACTION_POINTER_DOWN: { // We have a new multi-finger gesture // as orientation can change, query the metrics in touch down android.util.DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); mRightSlopEdge = metrics.widthPixels - mEdgeSlop; mBottomSlopEdge = metrics.heightPixels - mEdgeSlop; if (mPrevEvent != null) { mPrevEvent.recycle(); } mPrevEvent = android.view.MotionEvent.obtain(@event); mTimeDelta = 0; int index1 = @event.getActionIndex(); int index0 = @event.findPointerIndex(mActiveId0); mActiveId1 = @event.getPointerId(index1); if (index0 < 0 || index0 == index1) { // Probably someone sending us a broken event stream. index0 = findNewActiveIndex(@event, index0 == index1 ? -1 : mActiveId1, index0); mActiveId0 = @event.getPointerId(index0); } mActive0MostRecent = false; setContext(@event); // Check if we have a sloppy gesture. If so, delay // the beginning of the gesture until we're sure that's // what the user wanted. Sloppy gestures can happen if the // edge of the user's hand is touching the screen, for example. float edgeSlop = mEdgeSlop; float rightSlop = mRightSlopEdge; float bottomSlop = mBottomSlopEdge; float x0 = getRawX(@event, index0); float y0 = getRawY(@event, index0); float x1 = getRawX(@event, index1); float y1 = getRawY(@event, index1); bool p0sloppy = x0 < edgeSlop || y0 < edgeSlop || x0 > rightSlop || y0 > bottomSlop; bool p1sloppy = x1 < edgeSlop || y1 < edgeSlop || x1 > rightSlop || y1 > bottomSlop; if (p0sloppy && p1sloppy) { mFocusX = -1; mFocusY = -1; mSloppyGesture = true; } else { if (p0sloppy) { mFocusX = @event.getX(index1); mFocusY = @event.getY(index1); mSloppyGesture = true; } else { if (p1sloppy) { mFocusX = @event.getX(index0); mFocusY = @event.getY(index0); mSloppyGesture = true; } else { mSloppyGesture = false; mGestureInProgress = mListener.onScaleBegin(this); } } } break; } case android.view.MotionEvent.ACTION_MOVE: { if (mSloppyGesture) { // Initiate sloppy gestures if we've moved outside of the slop area. float edgeSlop = mEdgeSlop; float rightSlop = mRightSlopEdge; float bottomSlop = mBottomSlopEdge; int index0 = @event.findPointerIndex(mActiveId0); int index1 = @event.findPointerIndex(mActiveId1); float x0 = getRawX(@event, index0); float y0 = getRawY(@event, index0); float x1 = getRawX(@event, index1); float y1 = getRawY(@event, index1); bool p0sloppy = x0 < edgeSlop || y0 < edgeSlop || x0 > rightSlop || y0 > bottomSlop; bool p1sloppy = x1 < edgeSlop || y1 < edgeSlop || x1 > rightSlop || y1 > bottomSlop; if (p0sloppy) { // Do we have a different pointer that isn't sloppy? int index = findNewActiveIndex(@event, mActiveId1, index0); if (index >= 0) { index0 = index; mActiveId0 = @event.getPointerId(index); x0 = getRawX(@event, index); y0 = getRawY(@event, index); p0sloppy = false; } } if (p1sloppy) { // Do we have a different pointer that isn't sloppy? int index = findNewActiveIndex(@event, mActiveId0, index1); if (index >= 0) { index1 = index; mActiveId1 = @event.getPointerId(index); x1 = getRawX(@event, index); y1 = getRawY(@event, index); p1sloppy = false; } } if (p0sloppy && p1sloppy) { mFocusX = -1; mFocusY = -1; } else { if (p0sloppy) { mFocusX = @event.getX(index1); mFocusY = @event.getY(index1); } else { if (p1sloppy) { mFocusX = @event.getX(index0); mFocusY = @event.getY(index0); } else { mSloppyGesture = false; mGestureInProgress = mListener.onScaleBegin(this); } } } } break; } case android.view.MotionEvent.ACTION_POINTER_UP: { if (mSloppyGesture) { int pointerCount = @event.getPointerCount(); int actionIndex = @event.getActionIndex(); int actionId = @event.getPointerId(actionIndex); if (pointerCount > 2) { if (actionId == mActiveId0) { int newIndex = findNewActiveIndex(@event, mActiveId1, actionIndex); if (newIndex >= 0) { mActiveId0 = @event.getPointerId(newIndex); } } else { if (actionId == mActiveId1) { int newIndex = findNewActiveIndex(@event, mActiveId0, actionIndex); if (newIndex >= 0) { mActiveId1 = @event.getPointerId(newIndex); } } } } else { // Set focus point to the remaining finger int index = @event.findPointerIndex(actionId == mActiveId0 ? mActiveId1 : mActiveId0 ); if (index < 0) { mInvalidGesture = true; android.util.Log.e(TAG, "Invalid MotionEvent stream detected.", new System.Exception ()); if (mGestureInProgress) { mListener.onScaleEnd(this); } return false; } mActiveId0 = @event.getPointerId(index); mActive0MostRecent = true; mActiveId1 = -1; mFocusX = @event.getX(index); mFocusY = @event.getY(index); } } break; } } } else { switch (action) { case android.view.MotionEvent.ACTION_POINTER_DOWN: { // Transform gesture in progress - attempt to handle it // End the old gesture and begin a new one with the most recent two fingers. mListener.onScaleEnd(this); int oldActive0 = mActiveId0; int oldActive1 = mActiveId1; reset(); mPrevEvent = android.view.MotionEvent.obtain(@event); mActiveId0 = mActive0MostRecent ? oldActive0 : oldActive1; mActiveId1 = @event.getPointerId(@event.getActionIndex()); mActive0MostRecent = false; int index0 = @event.findPointerIndex(mActiveId0); if (index0 < 0 || mActiveId0 == mActiveId1) { // Probably someone sending us a broken event stream. android.util.Log.e(TAG, "Got " + android.view.MotionEvent.actionToString(action) + " with bad state while a gesture was in progress. " + "Did you forget to pass an event to " + "ScaleGestureDetector#onTouchEvent?"); index0 = findNewActiveIndex(@event, mActiveId0 == mActiveId1 ? -1 : mActiveId1, index0 ); mActiveId0 = @event.getPointerId(index0); } setContext(@event); mGestureInProgress = mListener.onScaleBegin(this); break; } case android.view.MotionEvent.ACTION_POINTER_UP: { int pointerCount = @event.getPointerCount(); int actionIndex = @event.getActionIndex(); int actionId = @event.getPointerId(actionIndex); bool gestureEnded = false; if (pointerCount > 2) { if (actionId == mActiveId0) { int newIndex = findNewActiveIndex(@event, mActiveId1, actionIndex); if (newIndex >= 0) { mListener.onScaleEnd(this); mActiveId0 = @event.getPointerId(newIndex); mActive0MostRecent = true; mPrevEvent = android.view.MotionEvent.obtain(@event); setContext(@event); mGestureInProgress = mListener.onScaleBegin(this); } else { gestureEnded = true; } } else { if (actionId == mActiveId1) { int newIndex = findNewActiveIndex(@event, mActiveId0, actionIndex); if (newIndex >= 0) { mListener.onScaleEnd(this); mActiveId1 = @event.getPointerId(newIndex); mActive0MostRecent = false; mPrevEvent = android.view.MotionEvent.obtain(@event); setContext(@event); mGestureInProgress = mListener.onScaleBegin(this); } else { gestureEnded = true; } } } mPrevEvent.recycle(); mPrevEvent = android.view.MotionEvent.obtain(@event); setContext(@event); } else { gestureEnded = true; } if (gestureEnded) { // Gesture ended setContext(@event); // Set focus point to the remaining finger int activeId = actionId == mActiveId0 ? mActiveId1 : mActiveId0; int index = @event.findPointerIndex(activeId); mFocusX = @event.getX(index); mFocusY = @event.getY(index); mListener.onScaleEnd(this); reset(); mActiveId0 = activeId; mActive0MostRecent = true; } break; } case android.view.MotionEvent.ACTION_CANCEL: { mListener.onScaleEnd(this); reset(); break; } case android.view.MotionEvent.ACTION_UP: { reset(); break; } case android.view.MotionEvent.ACTION_MOVE: { setContext(@event); // Only accept the event if our relative pressure is within // a certain limit - this can help filter shaky data as a // finger is lifted. if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) { bool updatePrevious = mListener.onScale(this); if (updatePrevious) { mPrevEvent.recycle(); mPrevEvent = android.view.MotionEvent.obtain(@event); } } break; } } } } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(@event, 0); } return handled; }
private void onSecondaryPointerUp(android.view.MotionEvent ev) { int activePointerIndex = ev.getActionIndex(); int pointerId = ev.getPointerId(activePointerIndex); if (pointerId == mActivePointerId) { int activeViewIndex = (mSwipeGestureType == GESTURE_SLIDE_DOWN) ? 0 : 1; android.view.View v = getViewAtRelativeIndex(activeViewIndex); if (v == null) { return; } { // Our primary pointer has gone up -- let's see if we can find // another pointer on the view. If so, then we should replace // our primary pointer with this new pointer and adjust things // so that the view doesn't jump for (int index = 0; index < ev.getPointerCount(); index++) { if (index != activePointerIndex) { float x = ev.getX(index); float y = ev.getY(index); mTouchRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); if (mTouchRect.contains(Sharpen.Util.Round(x), Sharpen.Util.Round(y))) { float oldX = ev.getX(activePointerIndex); float oldY = ev.getY(activePointerIndex); // adjust our frame of reference to avoid a jump mInitialY += (y - oldY); mInitialX += (x - oldX); mActivePointerId = ev.getPointerId(index); if (mVelocityTracker != null) { mVelocityTracker.clear(); } // ok, we're good, we found a new pointer which is touching the active view return; } } } } // if we made it this far, it means we didn't find a satisfactory new pointer :(, // so end the gesture handlePointerUp(ev); } }
private void setContext(android.view.MotionEvent curr) { if (mCurrEvent != null) { mCurrEvent.recycle(); } mCurrEvent = android.view.MotionEvent.obtain(curr); mCurrLen = -1; mPrevLen = -1; mScaleFactor = -1; android.view.MotionEvent prev = mPrevEvent; int prevIndex0 = prev.findPointerIndex(mActiveId0); int prevIndex1 = prev.findPointerIndex(mActiveId1); int currIndex0 = curr.findPointerIndex(mActiveId0); int currIndex1 = curr.findPointerIndex(mActiveId1); if (prevIndex0 < 0 || prevIndex1 < 0 || currIndex0 < 0 || currIndex1 < 0) { mInvalidGesture = true; android.util.Log.e(TAG, "Invalid MotionEvent stream detected.", new System.Exception ()); if (mGestureInProgress) { mListener.onScaleEnd(this); } return; } float px0 = prev.getX(prevIndex0); float py0 = prev.getY(prevIndex0); float px1 = prev.getX(prevIndex1); float py1 = prev.getY(prevIndex1); float cx0 = curr.getX(currIndex0); float cy0 = curr.getY(currIndex0); float cx1 = curr.getX(currIndex1); float cy1 = curr.getY(currIndex1); float pvx = px1 - px0; float pvy = py1 - py0; float cvx = cx1 - cx0; float cvy = cy1 - cy0; mPrevFingerDiffX = pvx; mPrevFingerDiffY = pvy; mCurrFingerDiffX = cvx; mCurrFingerDiffY = cvy; mFocusX = cx0 + cvx * 0.5f; mFocusY = cy0 + cvy * 0.5f; mTimeDelta = curr.getEventTime() - prev.getEventTime(); mCurrPressure = curr.getPressure(currIndex0) + curr.getPressure(currIndex1); mPrevPressure = prev.getPressure(prevIndex0) + prev.getPressure(prevIndex1); }
public override bool onTouchEvent(android.widget.TextView widget, android.text.Spannable buffer, android.view.MotionEvent @event) { int initialScrollX = -1; int initialScrollY = -1; int action = @event.getAction(); if (action == android.view.MotionEvent.ACTION_UP) { initialScrollX = android.text.method.Touch.getInitialScrollX(widget, buffer); initialScrollY = android.text.method.Touch.getInitialScrollY(widget, buffer); } bool handled = android.text.method.Touch.onTouchEvent(widget, buffer, @event); if (widget.isFocused() && !widget.didTouchFocusSelect()) { if (action == android.view.MotionEvent.ACTION_DOWN) { if (isSelecting(buffer)) { int offset = widget.getOffsetForPosition(@event.getX(), @event.getY()); buffer.setSpan(LAST_TAP_DOWN, offset, offset, android.text.SpannedClass.SPAN_POINT_POINT ); // Disallow intercepting of the touch events, so that // users can scroll and select at the same time. // without this, users would get booted out of select // mode once the view detected it needed to scroll. widget.getParent().requestDisallowInterceptTouchEvent(true); } } else { if (action == android.view.MotionEvent.ACTION_MOVE) { if (isSelecting(buffer) && handled) { // Before selecting, make sure we've moved out of the "slop". // handled will be true, if we're in select mode AND we're // OUT of the slop // Turn long press off while we're selecting. User needs to // re-tap on the selection to enable long press widget.cancelLongPress(); // Update selection as we're moving the selection area. // Get the current touch position int offset = widget.getOffsetForPosition(@event.getX(), @event.getY()); android.text.Selection.extendSelection(buffer, offset); return true; } } else { if (action == android.view.MotionEvent.ACTION_UP) { // If we have scrolled, then the up shouldn't move the cursor, // but we do need to make sure the cursor is still visible at // the current scroll offset to avoid the scroll jumping later // to show it. if ((initialScrollY >= 0 && initialScrollY != widget.getScrollY()) || (initialScrollX >= 0 && initialScrollX != widget.getScrollX())) { widget.moveCursorToVisibleOffset(); return true; } int offset = widget.getOffsetForPosition(@event.getX(), @event.getY()); if (isSelecting(buffer)) { buffer.removeSpan(LAST_TAP_DOWN); android.text.Selection.extendSelection(buffer, offset); } else { if (!widget.shouldIgnoreActionUpEvent()) { android.text.Selection.setSelection(buffer, offset); } } android.text.method.MetaKeyKeyListener.adjustMetaAfterKeypress(buffer); android.text.method.MetaKeyKeyListener.resetLockedMeta(buffer); return true; } } } } return handled; }
protected internal override bool dispatchHoverEvent(android.view.MotionEvent @event ) { int action = @event.getAction(); // First check whether the view group wants to intercept the hover event. bool interceptHover = onInterceptHoverEvent(@event); @event.setAction(action); // restore action in case it was changed android.view.MotionEvent eventNoHistory = @event; bool handled = false; // Send events to the hovered children and build a new list of hover targets until // one is found that handles the event. android.view.ViewGroup.HoverTarget firstOldHoverTarget = mFirstHoverTarget; mFirstHoverTarget = null; if (!interceptHover && action != android.view.MotionEvent.ACTION_HOVER_EXIT) { float x = @event.getX(); float y = @event.getY(); int childrenCount = mChildrenCount; if (childrenCount != 0) { android.view.View[] children = mChildren; android.view.ViewGroup.HoverTarget lastHoverTarget = null; { for (int i = childrenCount - 1; i >= 0; i--) { android.view.View child = children[i]; if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child , null)) { continue; } // Obtain a hover target for this child. Dequeue it from the // old hover target list if the child was previously hovered. android.view.ViewGroup.HoverTarget hoverTarget = firstOldHoverTarget; bool wasHovered; { for (android.view.ViewGroup.HoverTarget predecessor = null; ; ) { if (hoverTarget == null) { hoverTarget = android.view.ViewGroup.HoverTarget.obtain(child); wasHovered = false; break; } if (hoverTarget.child == child) { if (predecessor != null) { predecessor.next = hoverTarget.next; } else { firstOldHoverTarget = hoverTarget.next; } hoverTarget.next = null; wasHovered = true; break; } predecessor = hoverTarget; hoverTarget = hoverTarget.next; } } // Enqueue the hover target onto the new hover target list. if (lastHoverTarget != null) { lastHoverTarget.next = hoverTarget; } else { lastHoverTarget = hoverTarget; mFirstHoverTarget = hoverTarget; } // Dispatch the event to the child. if (action == android.view.MotionEvent.ACTION_HOVER_ENTER) { if (!wasHovered) { // Send the enter as is. handled |= dispatchTransformedGenericPointerEvent(@event, child); } } else { // enter if (action == android.view.MotionEvent.ACTION_HOVER_MOVE) { if (!wasHovered) { // Synthesize an enter from a move. eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory); eventNoHistory.setAction(android.view.MotionEvent.ACTION_HOVER_ENTER); handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, child); // enter eventNoHistory.setAction(action); handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, child); } else { // move // Send the move as is. handled |= dispatchTransformedGenericPointerEvent(@event, child); } } } if (handled) { break; } } } } } // Send exit events to all previously hovered children that are no longer hovered. while (firstOldHoverTarget != null) { android.view.View child = firstOldHoverTarget.child; // Exit the old hovered child. if (action == android.view.MotionEvent.ACTION_HOVER_EXIT) { // Send the exit as is. handled |= dispatchTransformedGenericPointerEvent(@event, child); } else { // exit // Synthesize an exit from a move or enter. // Ignore the result because hover focus has moved to a different view. if (action == android.view.MotionEvent.ACTION_HOVER_MOVE) { dispatchTransformedGenericPointerEvent(@event, child); } // move eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory); eventNoHistory.setAction(android.view.MotionEvent.ACTION_HOVER_EXIT); dispatchTransformedGenericPointerEvent(eventNoHistory, child); // exit eventNoHistory.setAction(action); } android.view.ViewGroup.HoverTarget nextOldHoverTarget = firstOldHoverTarget.next; firstOldHoverTarget.recycle(); firstOldHoverTarget = nextOldHoverTarget; } // Send events to the view group itself if no children have handled it. bool newHoveredSelf = !handled; if (newHoveredSelf == mHoveredSelf) { if (newHoveredSelf) { // Send event to the view group as before. handled |= base.dispatchHoverEvent(@event); } } else { if (mHoveredSelf) { // Exit the view group. if (action == android.view.MotionEvent.ACTION_HOVER_EXIT) { // Send the exit as is. handled |= base.dispatchHoverEvent(@event); } else { // exit // Synthesize an exit from a move or enter. // Ignore the result because hover focus is moving to a different view. if (action == android.view.MotionEvent.ACTION_HOVER_MOVE) { base.dispatchHoverEvent(@event); } // move eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory); eventNoHistory.setAction(android.view.MotionEvent.ACTION_HOVER_EXIT); base.dispatchHoverEvent(eventNoHistory); // exit eventNoHistory.setAction(action); } mHoveredSelf = false; } if (newHoveredSelf) { // Enter the view group. if (action == android.view.MotionEvent.ACTION_HOVER_ENTER) { // Send the enter as is. handled |= base.dispatchHoverEvent(@event); // enter mHoveredSelf = true; } else { if (action == android.view.MotionEvent.ACTION_HOVER_MOVE) { // Synthesize an enter from a move. eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory); eventNoHistory.setAction(android.view.MotionEvent.ACTION_HOVER_ENTER); handled |= base.dispatchHoverEvent(eventNoHistory); // enter eventNoHistory.setAction(action); handled |= base.dispatchHoverEvent(eventNoHistory); // move mHoveredSelf = true; } } } } // Recycle the copy of the event that we made. if (eventNoHistory != @event) { eventNoHistory.recycle(); } // Done. return handled; }
/// <summary> /// Will forward touch events to the delegate view if the event is within the bounds /// specified in the constructor. /// </summary> /// <remarks> /// Will forward touch events to the delegate view if the event is within the bounds /// specified in the constructor. /// </remarks> /// <param name="event">The touch event to forward</param> /// <returns>True if the event was forwarded to the delegate, false otherwise.</returns> public virtual bool onTouchEvent(android.view.MotionEvent @event) { int x = (int)@event.getX(); int y = (int)@event.getY(); bool sendToDelegate = false; bool hit = true; bool handled = false; switch (@event.getAction()) { case android.view.MotionEvent.ACTION_DOWN: { android.graphics.Rect bounds = mBounds; if (bounds.contains(x, y)) { mDelegateTargeted = true; sendToDelegate = true; } break; } case android.view.MotionEvent.ACTION_UP: case android.view.MotionEvent.ACTION_MOVE: { sendToDelegate = mDelegateTargeted; if (sendToDelegate) { android.graphics.Rect slopBounds = mSlopBounds; if (!slopBounds.contains(x, y)) { hit = false; } } break; } case android.view.MotionEvent.ACTION_CANCEL: { sendToDelegate = mDelegateTargeted; mDelegateTargeted = false; break; } } if (sendToDelegate) { android.view.View delegateView = mDelegateView; if (hit) { // Offset event coordinates to be inside the target view @event.setLocation(delegateView.getWidth() / 2, delegateView.getHeight() / 2); } else { // Offset event coordinates to be outside the target view (in case it does // something like tracking pressed state) int slop = mSlop; @event.setLocation(-(slop * 2), -(slop * 2)); } handled = delegateView.dispatchTouchEvent(@event); } return handled; }
public override bool dispatchTouchEvent(android.view.MotionEvent ev) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } bool handled = false; if (onFilterTouchEventForSecurity(ev)) { int action = ev.getAction(); int actionMasked = action & android.view.MotionEvent.ACTION_MASK; // Handle an initial down. if (actionMasked == android.view.MotionEvent.ACTION_DOWN) { // Throw away all previous state when starting a new touch gesture. // The framework may have dropped the up or cancel event for the previous gesture // due to an app switch, ANR, or some other state change. cancelAndClearTouchTargets(ev); resetTouchState(); } // Check for interception. bool intercepted; if (actionMasked == android.view.MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { bool disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action); } else { // restore action in case it was changed intercepted = false; } } else { // There are no touch targets and this action is not an initial down // so this view group continues to intercept touches. intercepted = true; } // Check for cancelation. bool canceled = resetCancelNextUpFlag(this) || actionMasked == android.view.MotionEvent .ACTION_CANCEL; // Update list of touch targets for pointer down, if needed. bool split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0; android.view.ViewGroup.TouchTarget newTouchTarget = null; bool alreadyDispatchedToNewTouchTarget = false; if (!canceled && !intercepted) { if (actionMasked == android.view.MotionEvent.ACTION_DOWN || (split && actionMasked == android.view.MotionEvent.ACTION_POINTER_DOWN) || actionMasked == android.view.MotionEvent .ACTION_HOVER_MOVE) { int actionIndex = ev.getActionIndex(); // always 0 for down int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) : android.view.ViewGroup .TouchTarget.ALL_POINTER_IDS; // Clean up earlier touch targets for this pointer id in case they // have become out of sync. removePointersFromTouchTargets(idBitsToAssign); int childrenCount = mChildrenCount; if (childrenCount != 0) { // Find a child that can receive the event. // Scan children from front to back. android.view.View[] children = mChildren; float x = ev.getX(actionIndex); float y = ev.getY(actionIndex); { for (int i = childrenCount - 1; i >= 0; i--) { android.view.View child = children[i]; if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child , null)) { continue; } newTouchTarget = getTouchTarget(child); if (newTouchTarget != null) { // Child is already receiving touch within its bounds. // Give it the new pointer in addition to the ones it is handling. newTouchTarget.pointerIdBits |= idBitsToAssign; break; } resetCancelNextUpFlag(child); if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. mLastTouchDownTime = ev.getDownTime(); mLastTouchDownIndex = i; mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; break; } } } } if (newTouchTarget == null && mFirstTouchTarget != null) { // Did not find a child to receive the event. // Assign the pointer to the least recently added target. newTouchTarget = mFirstTouchTarget; while (newTouchTarget.next != null) { newTouchTarget = newTouchTarget.next; } newTouchTarget.pointerIdBits |= idBitsToAssign; } } } // Dispatch to touch targets. if (mFirstTouchTarget == null) { // No touch targets so treat this as an ordinary view. handled = dispatchTransformedTouchEvent(ev, canceled, null, android.view.ViewGroup .TouchTarget.ALL_POINTER_IDS); } else { // Dispatch to touch targets, excluding the new touch target if we already // dispatched to it. Cancel touch targets if necessary. android.view.ViewGroup.TouchTarget predecessor = null; android.view.ViewGroup.TouchTarget target = mFirstTouchTarget; while (target != null) { android.view.ViewGroup.TouchTarget next = target.next; if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; } else { bool cancelChild = resetCancelNextUpFlag(target.child) || intercepted; if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits )) { handled = true; } if (cancelChild) { if (predecessor == null) { mFirstTouchTarget = next; } else { predecessor.next = next; } target.recycle(); target = next; continue; } } predecessor = target; target = next; } } // Update list of touch targets for pointer up or cancel, if needed. if (canceled || actionMasked == android.view.MotionEvent.ACTION_UP || actionMasked == android.view.MotionEvent.ACTION_HOVER_MOVE) { resetTouchState(); } else { if (split && actionMasked == android.view.MotionEvent.ACTION_POINTER_UP) { int actionIndex = ev.getActionIndex(); int idBitsToRemove = 1 << ev.getPointerId(actionIndex); removePointersFromTouchTargets(idBitsToRemove); } } } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1); } return handled; }
public override bool onInterceptTouchEvent(android.view.MotionEvent ev) { int action = ev.getAction(); if ((action == android.view.MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) { return true; } switch (action & android.view.MotionEvent.ACTION_MASK) { case android.view.MotionEvent.ACTION_MOVE: { int activePointerId = mActivePointerId; if (activePointerId == INVALID_POINTER) { // If we don't have a valid id, the touch down wasn't on content. break; } int pointerIndex = ev.findPointerIndex(activePointerId); float x = ev.getX(pointerIndex); int xDiff = (int)System.Math.Abs(x - mLastMotionX); if (xDiff > mTouchSlop) { mIsBeingDragged = true; mLastMotionX = x; initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); if (mParent != null) { mParent.requestDisallowInterceptTouchEvent(true); } } break; } case android.view.MotionEvent.ACTION_DOWN: { float x = ev.getX(); if (!inChild((int)x, (int)ev.getY())) { mIsBeingDragged = false; recycleVelocityTracker(); break; } mLastMotionX = x; mActivePointerId = ev.getPointerId(0); initOrResetVelocityTracker(); mVelocityTracker.addMovement(ev); mIsBeingDragged = !mScroller.isFinished(); break; } case android.view.MotionEvent.ACTION_CANCEL: case android.view.MotionEvent.ACTION_UP: { mIsBeingDragged = false; mActivePointerId = INVALID_POINTER; if (mScroller.springBack(mScrollX, mScrollY, 0, getScrollRange(), 0, 0)) { invalidate(); } break; } case android.view.MotionEvent.ACTION_POINTER_DOWN: { int index = ev.getActionIndex(); mLastMotionX = ev.getX(index); mActivePointerId = ev.getPointerId(index); break; } case android.view.MotionEvent.ACTION_POINTER_UP: { onSecondaryPointerUp(ev); mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId)); break; } } return mIsBeingDragged; }
public override bool onTouchEvent(android.view.MotionEvent @event) { int x = (int)@event.getX(); int y = (int)@event.getY(); if ((@event.getAction() == android.view.MotionEvent.ACTION_DOWN) && ((x < 0) || ( x >= this.getWidth()) || (y < 0) || (y >= this.getHeight()))) { this._enclosing.dismiss(); return true; } else { if (@event.getAction() == android.view.MotionEvent.ACTION_OUTSIDE) { this._enclosing.dismiss(); return true; } else { return base.onTouchEvent(@event); } } }
public override bool onTouchEvent(android.view.MotionEvent ev) { initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); int action = ev.getAction(); switch (action & android.view.MotionEvent.ACTION_MASK) { case android.view.MotionEvent.ACTION_DOWN: { mIsBeingDragged = getChildCount() != 0; if (!mIsBeingDragged) { return false; } if (!mScroller.isFinished()) { mScroller.abortAnimation(); } // Remember where the motion event started mLastMotionX = ev.getX(); mActivePointerId = ev.getPointerId(0); break; } case android.view.MotionEvent.ACTION_MOVE: { if (mIsBeingDragged) { // Scroll to follow the motion event int activePointerIndex = ev.findPointerIndex(mActivePointerId); float x = ev.getX(activePointerIndex); int deltaX = (int)(mLastMotionX - x); mLastMotionX = x; int oldX = mScrollX; int oldY = mScrollY; int range = getScrollRange(); int overscrollMode = getOverScrollMode(); bool canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS || (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0); if (overScrollBy(deltaX, 0, mScrollX, 0, range, 0, mOverscrollDistance, 0, true)) { // Break our velocity if we hit a scroll barrier. mVelocityTracker.clear(); } onScrollChanged(mScrollX, mScrollY, oldX, oldY); if (canOverscroll) { int pulledToX = oldX + deltaX; if (pulledToX < 0) { mEdgeGlowLeft.onPull((float)deltaX / getWidth()); if (!mEdgeGlowRight.isFinished()) { mEdgeGlowRight.onRelease(); } } else { if (pulledToX > range) { mEdgeGlowRight.onPull((float)deltaX / getWidth()); if (!mEdgeGlowLeft.isFinished()) { mEdgeGlowLeft.onRelease(); } } } if (mEdgeGlowLeft != null && (!mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished ())) { invalidate(); } } } break; } case android.view.MotionEvent.ACTION_UP: { if (mIsBeingDragged) { android.view.VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int initialVelocity = (int)velocityTracker.getXVelocity(mActivePointerId); if (getChildCount() > 0) { if ((System.Math.Abs(initialVelocity) > mMinimumVelocity)) { fling(-initialVelocity); } else { if (mScroller.springBack(mScrollX, mScrollY, 0, getScrollRange(), 0, 0)) { invalidate(); } } } mActivePointerId = INVALID_POINTER; mIsBeingDragged = false; recycleVelocityTracker(); if (mEdgeGlowLeft != null) { mEdgeGlowLeft.onRelease(); mEdgeGlowRight.onRelease(); } } break; } case android.view.MotionEvent.ACTION_CANCEL: { if (mIsBeingDragged && getChildCount() > 0) { if (mScroller.springBack(mScrollX, mScrollY, 0, getScrollRange(), 0, 0)) { invalidate(); } mActivePointerId = INVALID_POINTER; mIsBeingDragged = false; recycleVelocityTracker(); if (mEdgeGlowLeft != null) { mEdgeGlowLeft.onRelease(); mEdgeGlowRight.onRelease(); } } break; } case android.view.MotionEvent.ACTION_POINTER_UP: { onSecondaryPointerUp(ev); break; } } return true; }
private void trackTouchEvent(android.view.MotionEvent @event) { int width = getWidth(); int available = width - mPaddingLeft - mPaddingRight; int x = (int)@event.getX(); float scale; float progress = 0; if (x < mPaddingLeft) { scale = 0.0f; } else { if (x > width - mPaddingRight) { scale = 1.0f; } else { scale = (float)(x - mPaddingLeft) / (float)available; progress = mTouchProgressOffset; } } int max = getMax(); progress += scale * max; setProgress((int)progress, true); }
/// <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; }