Skip to content Skip to sidebar Skip to footer

Scalegesturedetector.ontouchevent Always Returns 'true'

I thought that the method 'onTouchEvent()' of a 'ScaleGestureDetector' instance was supposed to return 'true' only if it really handles the touch event, i. e. if it detects a multi

Solution 1:

Don't know if it's a bug or intentional, but that's definitely what the source does (ScaleGestureDetector.java:156):

public boolean onTouchEvent(MotionEvent event) {
   final int action = event.getAction();
   boolean handled = true;

   /* ... bunch of code that never updates 'handled' */

   return handled;
}

The way I solved this was to check all of the other types of touch events I might want to handle first, then invoke the gesture detector, e.g.

    if (mLongPressGestureDetector != null && mLongPressGestureDetector.onTouchEvent(event))
        return true;
    else if (mIsInMoveMode && mScaleGestureDetector != null) {
        // Check for a move
        if (action == MotionEvent.ACTION_MOVE && !mScaleGestureDetector.isInProgress()) {
            handleMove(event);
            return true;
        }

        // Now a scale
        mScaleGestureDetector.onTouchEvent(event);
        return true;
    }

Solution 2:

The ScaleGestureDetector provides the method isInProgress() that might do what you want...

Here is an example of it in use:

public boolean onTouch(View v, MotionEvent event) {

    mScaleDetector.onTouchEvent(event);

    if (!mScaleDetector.isInProgress()) {
        if (event.getAction() == MotionEvent.ACTION_DOWN || (event.getAction() == MotionEvent.ACTION_MOVE)) {
            touchX = (int) event.getX();
            touchY = (int) event.getY();
            isTouched = true;
        }

        if (event.getAction() == MotionEvent.ACTION_UP) {
            isTouched = false;
        }
    } else {
        isTouched = false;
    }

    return true;
}

Solution 3:

This is how I solved the problem: By overriding the Acitivity's onDispatchTouchEvent() method. Any other solution seemed not to work. The good thing about the onDispatchTouchEvent() method is that is always called prior to forwarding any touch events to any other receiver, so you can intercept every single touch event here.

If the event is handled somewhere in here (scale or swipe), I return immediately without forwarding the event to the super class, i. e. to the rest of the view hierarchy. If it is not, I forward it to the super class, so other views can handle it, e. g. to detect short or long clicks.

There were a few more problems to solve though: 1. If the user begins a scale gesture, I had to cancel any long click detection processes because the receiving view would get the first DOWN event, then nothing anymore (after the second finger comes down and the scaling started), and then spuriously think that a long press is being performed. 2. When a long press was performed and the context menu came up, I had to prevent swipe and scale gesture detection here in dispatchOnTouchEvent() until the next UP event, otherwise swiping and scaling would be performed even though the context menu is there.

Quite complicated, but I spent hours and hours and a lot of trial and error, and just could not find any simpler solution. Anyway, handling 1. scale gestures, 2. horizontal swipe gestures, 3. vertical scroll gestures, 4. long clicks and 5. short clicks, all on the same target view(s), is not quite simple a mission to accomplish...

Here is the code (relevant parts of it):

    @Override
public boolean dispatchTouchEvent(MotionEvent e) {
    if (eventInProgress) {
        // View shall only receive scale gesture event if visible
        if (targetView.isShown())
            scaleGestureDetector.onTouchEvent(e);
        if (scaleGestureDetector.isInProgress())
            motionEventConsumed = true;
    }

    if (motionEventConsumed) {
        if (e.getAction() == MotionEvent.ACTION_UP)
            motionEventConsumed = false;
        if (cancelLongPressEvent) {
            cancelLongPressEvent = false;
            targetView.cancelLongPress();
        }
        return (true);
    }

    // Get the action that was done on this touch event
    switch (e.getAction()) {
    case MotionEvent.ACTION_DOWN: {
        // store the X value when the user's finger was pressed down
        downXValue = e.getX();
        downYValue = e.getY();
        cancelLongPressEvent = true;
        eventInProgress = true;
        break;
    }

    case MotionEvent.ACTION_MOVE:
        // When having moved by too many x or y pixels, then
        // cancel any ongoing long klick events
        if (cancelLongPressEvent
                && Math.abs(e.getX() - downXValue)
                        + Math.abs(e.getY() - downYValue) > 40) {
            targetView.cancelLongPress();
            cancelLongPressEvent = false;
        }
        break;

    case MotionEvent.ACTION_UP: {
        if (eventInProgress) {
            // Get the X value when the user released his/her finger
            float deltaX = e.getX() - downXValue;
            float deltaY = e.getY() - downYValue;
            if (Math.abs(deltaX) > Math.abs(deltaY)
                    && Math.abs(deltaX) > 50) {
                // going backwards: pushing stuff to the right
                if (deltaX > 0) {
                    flipRight();
                    return (true);
                }
                // going forwards: pushing stuff to the left
                if (deltaX < 0) {
                    flipLeft();
                    return (true);
                }
                break;
            }
        }
    }
    }

    // If event was not handled here, then forward it to parent,
    // i. e. to view hierarchy
    return (super.dispatchTouchEvent(e));
}

[...]

@Override
public void onCreateContextMenu(ContextMenu menu, View v,
        ContextMenu.ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);
    MenuInflater mi = getMenuInflater();
    mi.inflate(R.menu.lztv_context_menu, menu);
    contextMenuTargetView = v;
    eventInProgress = false;
}

Post a Comment for "Scalegesturedetector.ontouchevent Always Returns 'true'"