13

I have a button and OnTouchListener attached to it. How can I find if motion (when user presses the button) happened inside or outside it? Both event.getAction() and event.getActionMasked() return only 0, 1 or 2, which is ActionDown, ActionUp, ActionMove, respectively. There's a constant MotionEvent.ACTION_OUTSIDE, which is 4, but somehow I don't receive it even if I drag touch outside the button - I still receive 2 from both methods. What's the problem?

UPD: I' ve found nice solution - just check focused state on view after ACTION_UP. If it's not focused, it means that movement happened outside the view.

Yury Pogrebnyak
  • 4,093
  • 9
  • 45
  • 78

7 Answers7

15

The MotionEvent.ACTION_OUTSIDE does not works for View's.

One solution is get the X and Y touch position and verify if it is inside the bounds of the View. It can be done like that:

@Override
public boolean onTouchEvent(MotionEvent e) {
    if (isInside(myView, e))
        Log.i(TAG, "TOUCH INSIDE");
    else
        Log.i(TAG, "TOUCH OUTSIDE");
    return true;
}

private boolean isInside(View v, MotionEvent e) {
    return !(e.getX() < 0 || e.getY() < 0
            || e.getX() > v.getMeasuredWidth()
            || e.getY() > v.getMeasuredHeight());
}
Derzu
  • 7,011
  • 3
  • 57
  • 60
  • isInside implementation is not working properly, parameters are not passed to isInside function, that answer really needs editing. There is one that worked for me https://stackoverflow.com/a/73111775/3969362 – Slion Feb 05 '23 at 19:28
9

That flag only applies to Windows, not Views. You will get ACTION_MOVE when you move your finger off the View, the event stays in the View it originated with. Look at the source code for SeekBar if you need clarification: even if you move your finger off the bar, the thumb still drags!

For doing this at the Window level use FLAG_WATCH_OUTSIDE_TOUCH, it works just fine.

Tom
  • 6,947
  • 7
  • 46
  • 76
8

case MotionEvent.ACTION_CANCEL worked for me.

ArieDov
  • 624
  • 6
  • 12
4

If the OnTouchListener is on the Button, you will only receive motion events from within the Button. The MotionEvent.ACTION_OUTSIDE will only be called when the motion event first goes outside the bounds of the View, and you should treat it like it was an ACTION_UP.

Jason Robinson
  • 31,005
  • 19
  • 77
  • 131
1
public static boolean touchWithinBounds(MotionEvent event, View view) {
    if (event == null || view == null || view.getWidth() == 0 || view.getHeight() == 0)
        return false;

    int[] viewLocation = new int[2];
    view.getLocationOnScreen(viewLocation);
    int viewMaxX = viewLocation[0] + view.getWidth() - 1;
    int viewMaxY = viewLocation[1] + view.getHeight() - 1;
    return (event.getRawX() <= viewMaxX && event.getRawX() >= viewLocation[0]
        && event.getRawY() <= viewMaxY && event.getRawY() >= viewLocation[1]);
}

Solution for when you're forwarding a touch event from a different view

hmac
  • 267
  • 3
  • 9
0

Try this One

private View.OnTouchListener handleTouch = new View.OnTouchListener() {

@Override
public boolean onTouch(View v, MotionEvent event) {

    int x = (int) event.getX();
    int y = (int) event.getY();

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            Log.i("TAG", "touched down");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.i("TAG", "moving: (" + x + ", " + y + ")");
            break;
        case MotionEvent.ACTION_UP:
            Log.i("TAG", "touched up");
            // do what you want when touch active
            break;
        case MotionEvent.ACTION_CANCEL:
            Log.if("TAG","touched canceled")
            // outside of the view 
            //ACTION_MOVE present 
            break:

    }

    return true;
}

};

Tarif Chakder
  • 1,708
  • 1
  • 11
  • 10
0

Here is a Kotlin extension function you can use from your touch listener to check if the provided event is inside the given view:

private fun MotionEvent.isInside(v: View): Boolean {
    return Rect().apply(v::getGlobalVisibleRect).contains(rawX.toInt(), rawY.toInt())
}
Slion
  • 2,558
  • 2
  • 23
  • 27