7

In my Android app I have an ImageView where I'd like the user to be able to fling it left/right/up/down to change the image (static maps) to the adjacent one. But in addition, I'd like pinch-zoom abilities and a map itself.

I can get either flinging OR pinch-zooming to work, but not together. I'm using GestureDetector (with a SimpleOnGestureListener) for the flinging. And I'm using ScaleGestureDetector (from Making Sense of Multitouch) for the scaling.

The difficulty is to determine which gesture listener to invoke upon a touch action. This is less a coding issue, but logic issue. Upon a single finger touch action, is it a fling or scale? Even when a pinch-zoom is used, the initial MotionEvent is ACTION_DOWN. I've been trying to use the image size (intrinsic or scaled?) as a decision point. But the initial scaling operation (when image size is intrinsic and I want to zoom on it) with ACTION_DOWN seems to escape me.

Has anyone tackled this successfully previously?

mraviator
  • 3,954
  • 8
  • 36
  • 51

3 Answers3

5

You can pass the events on to both gesture detectors.

Check http://developer.android.com/training/gestures/scale.html under "More complex scaling example":

public boolean onTouchEvent(MotionEvent event) {
    boolean retVal = mScaleGestureDetector.onTouchEvent(event);
    retVal = mGestureDetector.onTouchEvent(event) || retVal;
    return retVal || super.onTouchEvent(event);
}

Of course given the bug Ratatat is referencing, super.onTouchEvent will never be called in the above example, which may or may not be fine, depending on your use case.

aij
  • 4,278
  • 28
  • 38
  • 1
    I tried this and Ratatat's answer, and this one gave better results for me. When handling simultaneous panning and zooming, if you use this answer, you can just handle any panning in the onScroll callback and any zooming in onScale, rather than trying to pan based on movement in the focusX/focusY of the scale action. – Ben Dilts Jan 07 '15 at 22:39
  • If you want both sides of the OR operator to be called, you can use `|` instead of `||`. Like: `return mScaleGestureDetector.onTouchEvent(event) | mGestureDetector.onTouchEvent(event) | super.onTouchEvent(event);` – Robyer Jan 29 '20 at 10:55
3

The idea of Ratatat's answer is OK but we should still pass events to the gestureDetector even if we don't want to scroll, or it will be messed up.

I ended up with something like this:

scaleDetector = new ScaleGestureDetector( ... );

gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        if (scaleDetector.isInProgress()) {
            // don't allow scrolling while scaling
            return false;
        }

        // handle scrolling

        return true;
    }
}

And then onTouchEvent's implementation should be like in aij's answer:

boolean result = scaleDetector.onTouchEvent(event);
result = gestureDetector.onTouchEvent(event) || result;
return result || super.onTouchEvent(event);
yonix
  • 9,925
  • 7
  • 30
  • 50
1

Finally found the answer on a link: http://code.google.com/p/android/issues/detail?id=42591

@Override
public boolean onTouchEvent(MotionEvent event) {
    boolean result = mScaleGestureDetector.onTouchEvent(event);

    // result is always true here, so I need another way to check for a detected scaling gesture
    boolean isScaling = result = mScaleGestureDetector.isInProgress();
    if (!isScaling) {
        // if no scaling is performed check for other gestures (fling, long tab, etc.)
        result = mCommonGestureDetector.onTouchEvent(event);
    }

    // some irrelevant checks...

    return result ? result : super.onTouchEvent(event);
}
Illegal Argument
  • 9,249
  • 2
  • 38
  • 56
Ratatat Richie
  • 111
  • 1
  • 9