Example usage for android.view MotionEvent getY

List of usage examples for android.view MotionEvent getY

Introduction

In this page you can find the example usage for android.view MotionEvent getY.

Prototype

public final float getY(int pointerIndex) 

Source Link

Document

Returns the Y coordinate of this event for the given pointer index (use #getPointerId(int) to find the pointer identifier for this index).

Usage

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);
}