List of usage examples for android.view MotionEvent getY
public final float getY(int pointerIndex)
From source file:com.android.systemui.statusbar.phone.NotificationPanelView.java
private void onQsTouch(MotionEvent event) { int pointerIndex = event.findPointerIndex(mTrackingPointer); if (pointerIndex < 0) { pointerIndex = 0;/*from ww w. ja va2 s.c o m*/ mTrackingPointer = event.getPointerId(pointerIndex); } final float y = event.getY(pointerIndex); final float x = event.getX(pointerIndex); final float h = y - mInitialTouchY; logf("onQsTouch() touch event = " + event.getActionMasked()); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: logf("onQsTouch() touch event = MotionEvent.ACTION_DOWN "); mQsTracking = true; mInitialTouchY = y; mInitialTouchX = x; onQsExpansionStarted(); mInitialHeightOnTouch = mQsExpansionHeight; initVelocityTracker(); trackMovement(event); break; case MotionEvent.ACTION_POINTER_UP: logf("onQsTouch() touch event = MotionEvent.ACTION_POINTER_UP "); final int upPointer = event.getPointerId(event.getActionIndex()); if (mTrackingPointer == upPointer) { // gesture is ongoing, find a new pointer to track final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; final float newY = event.getY(newIndex); final float newX = event.getX(newIndex); mTrackingPointer = event.getPointerId(newIndex); mInitialHeightOnTouch = mQsExpansionHeight; mInitialTouchY = newY; mInitialTouchX = newX; } break; case MotionEvent.ACTION_MOVE: logf("onQsTouch() touch event = MotionEvent.ACTION_MOVE "); setQsExpansion(h + mInitialHeightOnTouch); if (h >= getFalsingThreshold()) { mQsTouchAboveFalsingThreshold = true; } trackMovement(event); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: logf("onQsTouch() touch event = MotionEvent.ACTION_UP/ACTION_CANCEL"); mQsTracking = false; mTrackingPointer = -1; trackMovement(event); float fraction = getQsExpansionFraction(); if ((fraction != 0f || y >= mInitialTouchY) && (fraction != 1f || y <= mInitialTouchY)) { flingQsWithCurrentVelocity(y, event.getActionMasked() == MotionEvent.ACTION_CANCEL); } else { logQsSwipeDown(y); mScrollYOverride = -1; } if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } break; } }
From source file:com.android.systemui.statusbar.phone.NotificationPanelView.java
private boolean onQsIntercept(MotionEvent event) { int pointerIndex = event.findPointerIndex(mTrackingPointer); if (pointerIndex < 0) { pointerIndex = 0;/*from w ww .j av a 2 s . c om*/ mTrackingPointer = event.getPointerId(pointerIndex); } final float x = event.getX(pointerIndex); final float y = event.getY(pointerIndex); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: mIntercepting = true; mInitialTouchY = y; mInitialTouchX = x; initVelocityTracker(); trackMovement(event); if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) { getParent().requestDisallowInterceptTouchEvent(true); } if (mQsExpansionAnimator != null) { onQsExpansionStarted(); mInitialHeightOnTouch = mQsExpansionHeight; mQsTracking = true; mIntercepting = false; mNotificationStackScroller.removeLongPressCallback(); } break; case MotionEvent.ACTION_POINTER_UP: final int upPointer = event.getPointerId(event.getActionIndex()); if (mTrackingPointer == upPointer) { // gesture is ongoing, find a new pointer to track final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; mTrackingPointer = event.getPointerId(newIndex); mInitialTouchX = event.getX(newIndex); mInitialTouchY = event.getY(newIndex); } break; case MotionEvent.ACTION_MOVE: final float h = y - mInitialTouchY; trackMovement(event); if (mQsTracking) { // Already tracking because onOverscrolled was called. We need to update here // so we don't stop for a frame until the next touch event gets handled in // onTouchEvent. setQsExpansion(h + mInitialHeightOnTouch); trackMovement(event); mIntercepting = false; return true; } if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX) && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) { mQsTracking = true; onQsExpansionStarted(); notifyExpandingFinished(); mInitialHeightOnTouch = mQsExpansionHeight; mInitialTouchY = y; mInitialTouchX = x; mIntercepting = false; mNotificationStackScroller.removeLongPressCallback(); return true; } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: trackMovement(event); if (mQsTracking) { flingQsWithCurrentVelocity(y, event.getActionMasked() == MotionEvent.ACTION_CANCEL); mQsTracking = false; } mIntercepting = false; break; } return false; }
From source file:cc.flydev.launcher.Page.java
protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) { // Disallow scrolling if we don't have a valid pointer index final int pointerIndex = ev.findPointerIndex(mActivePointerId); if (pointerIndex == -1) return;//from w ww. j a va 2 s.c o m // Disallow scrolling if we started the gesture from outside the viewport final float x = ev.getX(pointerIndex); final float y = ev.getY(pointerIndex); if (!isTouchPointInViewportWithBuffer((int) x, (int) y)) return; final int xDiff = (int) Math.abs(x - mLastMotionX); final int yDiff = (int) Math.abs(y - mLastMotionY); final int touchSlop = Math.round(touchSlopScale * mTouchSlop); boolean xPaged = xDiff > mPagingTouchSlop; boolean xMoved = xDiff > touchSlop; boolean yMoved = yDiff > touchSlop; if (xMoved || xPaged || yMoved) { if (mUsePagingTouchSlop ? xPaged : xMoved) { // Scroll if the user moved far enough along the X axis mTouchState = TOUCH_STATE_SCROLLING; mTotalMotionX += Math.abs(mLastMotionX - x); mLastMotionX = x; mLastMotionXRemainder = 0; mTouchX = getViewportOffsetX() + getScrollX(); mSmoothingTime = System.nanoTime() / NANOTIME_DIV; pageBeginMoving(); } } }
From source file:xiaofan.llongimageview.view.SubsamplingScaleImageView.java
/** * Handle touch events. One finger pans, and two finger pinch and zoom plus panning. *///from w w w. j a v a 2s .c om @Override public boolean onTouchEvent(MotionEvent event) { PointF vCenterEnd; float vDistEnd; // During non-interruptible anims, ignore all touch events if (anim != null && !anim.interruptible) { getParent().requestDisallowInterceptTouchEvent(true); return true; } else { anim = null; } // Abort if not ready if (vTranslate == null) { return true; } // Detect flings, taps and double taps if (detector == null || detector.onTouchEvent(event)) { return true; } int touchCount = event.getPointerCount(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_1_DOWN: case MotionEvent.ACTION_POINTER_2_DOWN: anim = null; getParent().requestDisallowInterceptTouchEvent(true); maxTouchCount = Math.max(maxTouchCount, touchCount); if (touchCount >= 2) { if (zoomEnabled) { // Start pinch to zoom. Calculate distance between touch points and center point of the pinch. float distance = distance(event.getX(0), event.getX(1), event.getY(0), event.getY(1)); scaleStart = scale; vDistStart = distance; vTranslateStart = new PointF(vTranslate.x, vTranslate.y); vCenterStart = new PointF((event.getX(0) + event.getX(1)) / 2, (event.getY(0) + event.getY(1)) / 2); } else { // Abort all gestures on second touch maxTouchCount = 0; } // Cancel long click timer handler.removeMessages(MESSAGE_LONG_CLICK); } else { // Start one-finger pan vTranslateStart = new PointF(vTranslate.x, vTranslate.y); vCenterStart = new PointF(event.getX(), event.getY()); // Start long click timer handler.sendEmptyMessageDelayed(MESSAGE_LONG_CLICK, 600); } return true; case MotionEvent.ACTION_MOVE: boolean consumed = false; if (maxTouchCount > 0) { if (touchCount >= 2) { // Calculate new distance between touch points, to scale and pan relative to start values. vDistEnd = distance(event.getX(0), event.getX(1), event.getY(0), event.getY(1)); vCenterEnd = new PointF((event.getX(0) + event.getX(1)) / 2, (event.getY(0) + event.getY(1)) / 2); if (zoomEnabled && (distance(vCenterStart.x, vCenterEnd.x, vCenterStart.y, vCenterEnd.y) > 5 || Math.abs(vDistEnd - vDistStart) > 5 || isPanning)) { isZooming = true; isPanning = true; consumed = true; scale = Math.min(maxScale, (vDistEnd / vDistStart) * scaleStart); if (scale <= minScale()) { // Minimum scale reached so don't pan. Adjust start settings so any expand will zoom in. vDistStart = vDistEnd; scaleStart = minScale(); vCenterStart = vCenterEnd; vTranslateStart = vTranslate; } else if (panEnabled) { // Translate to place the source image coordinate that was at the center of the pinch at the start // at the center of the pinch now, to give simultaneous pan + zoom. float vLeftStart = vCenterStart.x - vTranslateStart.x; float vTopStart = vCenterStart.y - vTranslateStart.y; float vLeftNow = vLeftStart * (scale / scaleStart); float vTopNow = vTopStart * (scale / scaleStart); vTranslate.x = vCenterEnd.x - vLeftNow; vTranslate.y = vCenterEnd.y - vTopNow; } else if (sRequestedCenter != null) { // With a center specified from code, zoom around that point. vTranslate.x = (getWidth() / 2) - (scale * sRequestedCenter.x); vTranslate.y = (getHeight() / 2) - (scale * sRequestedCenter.y); } else { // With no requested center, scale around the image center. vTranslate.x = (getWidth() / 2) - (scale * (sWidth() / 2)); vTranslate.y = (getHeight() / 2) - (scale * (sHeight() / 2)); } fitToBounds(true); refreshRequiredTiles(false); } } else if (!isZooming) { // One finger pan - translate the image. We do this calculation even with pan disabled so click // and long click behaviour is preserved. float dx = Math.abs(event.getX() - vCenterStart.x); float dy = Math.abs(event.getY() - vCenterStart.y); if (dx > 5 || dy > 5 || isPanning) { consumed = true; vTranslate.x = vTranslateStart.x + (event.getX() - vCenterStart.x); vTranslate.y = vTranslateStart.y + (event.getY() - vCenterStart.y); float lastX = vTranslate.x; float lastY = vTranslate.y; fitToBounds(true); if (lastX == vTranslate.x || (lastY == vTranslate.y && dy > 10) || isPanning) { isPanning = true; } else if (dx > 5) { // Haven't panned the image, and we're at the left or right edge. Switch to page swipe. maxTouchCount = 0; handler.removeMessages(MESSAGE_LONG_CLICK); getParent().requestDisallowInterceptTouchEvent(false); } if (!panEnabled) { vTranslate.x = vTranslateStart.x; vTranslate.y = vTranslateStart.y; getParent().requestDisallowInterceptTouchEvent(false); } refreshRequiredTiles(false); } } } if (consumed) { handler.removeMessages(MESSAGE_LONG_CLICK); invalidate(); return true; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_POINTER_2_UP: handler.removeMessages(MESSAGE_LONG_CLICK); if (maxTouchCount > 0 && (isZooming || isPanning)) { if (isZooming && touchCount == 2) { // Convert from zoom to pan with remaining touch isPanning = true; vTranslateStart = new PointF(vTranslate.x, vTranslate.y); if (event.getActionIndex() == 1) { vCenterStart = new PointF(event.getX(0), event.getY(0)); } else { vCenterStart = new PointF(event.getX(1), event.getY(1)); } } if (touchCount < 3) { // End zooming when only one touch point isZooming = false; } if (touchCount < 2) { // End panning when no touch points isPanning = false; maxTouchCount = 0; } // Trigger load of tiles now required refreshRequiredTiles(true); return true; } if (touchCount == 1) { isZooming = false; isPanning = false; maxTouchCount = 0; } return true; } return super.onTouchEvent(event); }
From source file:cn.ismartv.tvrecyclerview.widget.helper.ItemTouchHelper.java
/** * Checks whether we should select a View for swiping. *///from ww w .ja va 2 s . c o m private boolean checkSelectForSwipe(int action, MotionEvent motionEvent, int pointerIndex) { if (mSelected != null || action != MotionEvent.ACTION_MOVE || mActionState == ACTION_STATE_DRAG || !mCallback.isItemViewSwipeEnabled()) { return false; } if (mRecyclerView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) { return false; } final ViewHolder vh = findSwipedView(motionEvent); if (vh == null) { return false; } final int movementFlags = mCallback.getAbsoluteMovementFlags(mRecyclerView, vh); final int swipeFlags = (movementFlags & ACTION_MODE_SWIPE_MASK) >> (DIRECTION_FLAG_COUNT * ACTION_STATE_SWIPE); if (swipeFlags == 0) { return false; } // mDx and mDy are only set in allowed directions. We use custom x/y here instead of // updateDxDy to avoid swiping if user moves more in the other direction final float x = motionEvent.getX(pointerIndex); final float y = motionEvent.getY(pointerIndex); // Calculate the distance moved final float dx = x - mInitialTouchX; final float dy = y - mInitialTouchY; // swipe target is chose w/o applying flags so it does not really check if swiping in that // direction is allowed. This why here, we use mDx mDy to check slope value again. final float absDx = Math.abs(dx); final float absDy = Math.abs(dy); if (absDx < mSlop && absDy < mSlop) { return false; } if (absDx > absDy) { if (dx < 0 && (swipeFlags & LEFT) == 0) { return false; } if (dx > 0 && (swipeFlags & RIGHT) == 0) { return false; } } else { if (dy < 0 && (swipeFlags & UP) == 0) { return false; } if (dy > 0 && (swipeFlags & DOWN) == 0) { return false; } } mDx = mDy = 0f; mActivePointerId = motionEvent.getPointerId(0); select(vh, ACTION_STATE_SWIPE); return true; }
From source file:cc.flydev.launcher.Page.java
private void onSecondaryPointerUp(MotionEvent ev) { final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; final 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. final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mLastMotionX = mDownMotionX = ev.getX(newPointerIndex); mLastMotionY = ev.getY(newPointerIndex); mLastMotionXRemainder = 0;//from w w w . jav a 2s . co m mActivePointerId = ev.getPointerId(newPointerIndex); if (mVelocityTracker != null) { mVelocityTracker.clear(); } } }
From source file:com.anysoftkeyboard.keyboards.views.AnyKeyboardViewBase.java
@Override public boolean onTouchEvent(@NonNull MotionEvent nativeMotionEvent) { if (mKeyboard == null)//I mean, if there isn't any keyboard I'm handling, what's the point? return false; final int action = MotionEventCompat.getActionMasked(nativeMotionEvent); final int pointerCount = MotionEventCompat.getPointerCount(nativeMotionEvent); if (pointerCount > 1) mLastTimeHadTwoFingers = SystemClock.elapsedRealtime();//marking the time. Read isAtTwoFingersState() if (mTouchesAreDisabledTillLastFingerIsUp) { if (!areTouchesDisabled(nativeMotionEvent)/*this means it was just reset*/) { mTouchesAreDisabledTillLastFingerIsUp = false; //continue with onTouchEvent flow. if (action != MotionEvent.ACTION_DOWN) { //swallowing the event. //in case this is a DOWN event, we do want to pass it return true; }/*ww w. j a v a2 s . c om*/ } else { //swallowing touch event until we reset mTouchesAreDisabledTillLastFingerIsUp return true; } } final long eventTime = nativeMotionEvent.getEventTime(); final int index = MotionEventCompat.getActionIndex(nativeMotionEvent); final int id = nativeMotionEvent.getPointerId(index); final int x = (int) nativeMotionEvent.getX(index); final int y = (int) nativeMotionEvent.getY(index); if (mKeyPressTimingHandler.isInKeyRepeat()) { // It will keep being in the key repeating mode while the key is // being pressed. if (action == MotionEvent.ACTION_MOVE) { return true; } final PointerTracker tracker = getPointerTracker(id); // Key repeating timer will be canceled if 2 or more keys are in // action, and current // event (UP or DOWN) is non-modifier key. if (pointerCount > 1 && !tracker.isModifier()) { mKeyPressTimingHandler.cancelKeyRepeatTimer(); } // Up event will pass through. } if (action == MotionEvent.ACTION_MOVE) { for (int i = 0; i < pointerCount; i++) { PointerTracker tracker = getPointerTracker(nativeMotionEvent.getPointerId(i)); tracker.onMoveEvent((int) nativeMotionEvent.getX(i), (int) nativeMotionEvent.getY(i)); } } else { PointerTracker tracker = getPointerTracker(id); sendOnXEvent(action, eventTime, x, y, tracker); } return true; }
From source file:caesar.feng.framework.widget.StaggeredGrid.ExtendableListView.java
private void onSecondaryPointerUp(MotionEvent event) { final int pointerIndex = (event.getAction() & MotionEventCompat.ACTION_POINTER_INDEX_MASK) >> MotionEventCompat.ACTION_POINTER_INDEX_SHIFT; final int pointerId = event.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. final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mMotionX = (int) event.getX(newPointerIndex); mMotionY = (int) event.getY(newPointerIndex); mActivePointerId = event.getPointerId(newPointerIndex); recycleVelocityTracker();/*from ww w . j a va 2 s . c o m*/ } }
From source file:com.android.launcher3.ItemTouchHelper.java
/** * Checks whether we should select a View for swiping. *///from w w w . j a v a 2 s . c om boolean checkSelectForSwipe(int action, MotionEvent motionEvent, int pointerIndex) { if (mSelected != null || action != MotionEvent.ACTION_MOVE || mActionState == ACTION_STATE_DRAG || !mCallback.isItemViewSwipeEnabled()) { return false; } if (mRecyclerView.getScrollState() == android.support.v7.widget.RecyclerView.SCROLL_STATE_DRAGGING) { return false; } final android.support.v7.widget.RecyclerView.ViewHolder vh = findSwipedView(motionEvent); if (vh == null) { return false; } final int movementFlags = mCallback.getAbsoluteMovementFlags(mRecyclerView, vh); final int swipeFlags = (movementFlags & ACTION_MODE_SWIPE_MASK) >> (DIRECTION_FLAG_COUNT * ACTION_STATE_SWIPE); if (swipeFlags == 0) { return false; } // mDx and mDy are only set in allowed directions. We use custom x/y here instead of // updateDxDy to avoid swiping if user moves more in the other direction final float x = motionEvent.getX(pointerIndex); final float y = motionEvent.getY(pointerIndex); // Calculate the distance moved final float dx = x - mInitialTouchX; final float dy = y - mInitialTouchY; // swipe target is chose w/o applying flags so it does not really check if swiping in that // direction is allowed. This why here, we use mDx mDy to check slope value again. final float absDx = Math.abs(dx); final float absDy = Math.abs(dy); if (absDx < mSlop && absDy < mSlop) { return false; } if (absDx > absDy) { if (dx < 0 && (swipeFlags & LEFT) == 0) { return false; } if (dx > 0 && (swipeFlags & RIGHT) == 0) { return false; } } else { if (dy < 0 && (swipeFlags & UP) == 0) { return false; } if (dy > 0 && (swipeFlags & DOWN) == 0) { return false; } } mDx = mDy = 0f; mActivePointerId = motionEvent.getPointerId(0); select(vh, ACTION_STATE_SWIPE); return true; }
From source file:com.bizcom.vc.widget.cus.SubsamplingScaleImageView.java
/** * Handle touch events. One finger pans, and two finger pinch and zoom plus * panning./*ww w . j av a 2 s . co m*/ */ @Override public boolean onTouchEvent(MotionEvent event) { PointF vCenterEnd; float vDistEnd; // During non-interruptible anims, ignore all touch events if (anim != null && !anim.interruptible) { getParent().requestDisallowInterceptTouchEvent(true); return true; } else { anim = null; } // Abort if not ready if (vTranslate == null) { return true; } // Detect flings, taps and double taps if (detector == null || detector.onTouchEvent(event)) { return true; } int touchCount = event.getPointerCount(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_1_DOWN: case MotionEvent.ACTION_POINTER_2_DOWN: anim = null; getParent().requestDisallowInterceptTouchEvent(true); maxTouchCount = Math.max(maxTouchCount, touchCount); if (touchCount >= 2) { if (zoomEnabled) { // Start pinch to zoom. Calculate distance between touch // points and center point of the pinch. float distance = distance(event.getX(0), event.getX(1), event.getY(0), event.getY(1)); scaleStart = scale; vDistStart = distance; vTranslateStart = new PointF(vTranslate.x, vTranslate.y); vCenterStart = new PointF((event.getX(0) + event.getX(1)) / 2, (event.getY(0) + event.getY(1)) / 2); } else { // Abort all gestures on second touch maxTouchCount = 0; } // Cancel long click timer handler.removeMessages(MESSAGE_LONG_CLICK); } else { // Start one-finger pan vTranslateStart = new PointF(vTranslate.x, vTranslate.y); vCenterStart = new PointF(event.getX(), event.getY()); // Start long click timer handler.sendEmptyMessageDelayed(MESSAGE_LONG_CLICK, 600); } return true; case MotionEvent.ACTION_MOVE: boolean consumed = false; if (maxTouchCount > 0) { if (touchCount >= 2) { // Calculate new distance between touch points, to scale and // pan relative to start values. vDistEnd = distance(event.getX(0), event.getX(1), event.getY(0), event.getY(1)); vCenterEnd = new PointF((event.getX(0) + event.getX(1)) / 2, (event.getY(0) + event.getY(1)) / 2); if (zoomEnabled && (distance(vCenterStart.x, vCenterEnd.x, vCenterStart.y, vCenterEnd.y) > 5 || Math.abs(vDistEnd - vDistStart) > 5 || isPanning)) { isZooming = true; isPanning = true; consumed = true; scale = Math.min(maxScale, (vDistEnd / vDistStart) * scaleStart); if (scale <= minScale()) { // Minimum scale reached so don't pan. Adjust start // settings so any expand will zoom in. vDistStart = vDistEnd; scaleStart = minScale(); vCenterStart = vCenterEnd; vTranslateStart = vTranslate; } else if (panEnabled) { // Translate to place the source image coordinate // that was at the center of the pinch at the start // at the center of the pinch now, to give // simultaneous pan + zoom. float vLeftStart = vCenterStart.x - vTranslateStart.x; float vTopStart = vCenterStart.y - vTranslateStart.y; float vLeftNow = vLeftStart * (scale / scaleStart); float vTopNow = vTopStart * (scale / scaleStart); vTranslate.x = vCenterEnd.x - vLeftNow; vTranslate.y = vCenterEnd.y - vTopNow; } else if (sRequestedCenter != null) { // With a center specified from code, zoom around // that point. vTranslate.x = (getWidth() / 2) - (scale * sRequestedCenter.x); vTranslate.y = (getHeight() / 2) - (scale * sRequestedCenter.y); } else { // With no requested center, scale around the image // center. vTranslate.x = (getWidth() / 2) - (scale * (sWidth() / 2)); vTranslate.y = (getHeight() / 2) - (scale * (sHeight() / 2)); } fitToBounds(true); refreshRequiredTiles(false); } } else if (!isZooming) { // One finger pan - translate the image. We do this // calculation even with pan disabled so click // and long click behaviour is preserved. float dx = Math.abs(event.getX() - vCenterStart.x); float dy = Math.abs(event.getY() - vCenterStart.y); if (dx > 5 || dy > 5 || isPanning) { consumed = true; vTranslate.x = vTranslateStart.x + (event.getX() - vCenterStart.x); vTranslate.y = vTranslateStart.y + (event.getY() - vCenterStart.y); float lastX = vTranslate.x; float lastY = vTranslate.y; fitToBounds(true); if (lastX == vTranslate.x || (lastY == vTranslate.y && dy > 10) || isPanning) { isPanning = true; } else if (dx > 5) { // Haven't panned the image, and we're at the left // or right edge. Switch to page swipe. maxTouchCount = 0; handler.removeMessages(MESSAGE_LONG_CLICK); getParent().requestDisallowInterceptTouchEvent(false); } if (!panEnabled) { vTranslate.x = vTranslateStart.x; vTranslate.y = vTranslateStart.y; getParent().requestDisallowInterceptTouchEvent(false); } refreshRequiredTiles(false); } } } if (consumed) { handler.removeMessages(MESSAGE_LONG_CLICK); invalidate(); return true; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_POINTER_2_UP: handler.removeMessages(MESSAGE_LONG_CLICK); if (maxTouchCount > 0 && (isZooming || isPanning)) { if (isZooming && touchCount == 2) { // Convert from zoom to pan with remaining touch isPanning = true; vTranslateStart = new PointF(vTranslate.x, vTranslate.y); if (event.getActionIndex() == 1) { vCenterStart = new PointF(event.getX(0), event.getY(0)); } else { vCenterStart = new PointF(event.getX(1), event.getY(1)); } } if (touchCount < 3) { // End zooming when only one touch point isZooming = false; } if (touchCount < 2) { // End panning when no touch points isPanning = false; maxTouchCount = 0; } // Trigger load of tiles now required refreshRequiredTiles(true); return true; } if (touchCount == 1) { isZooming = false; isPanning = false; maxTouchCount = 0; } return true; } return super.onTouchEvent(event); }