Android: detection if user touches and pulls out button area?

In Android, how can we detect if a user has touched a button and pulled out of the button area?

+41
android button gestures
Jun 20 2018-11-11T00:
source share
8 answers

Check MotionEvent.MOVE_OUTSIDE: Check MotionEvent.MOVE:

private Rect rect; // Variable rect to hold the bounds of the view public boolean onTouch(View v, MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_DOWN){ // Construct a rect of the view bounds rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); } if(event.getAction() == MotionEvent.ACTION_MOVE){ if(!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){ // User moved outside bounds } } return false; } 

NOTE. If you want to target Android 4.0, a whole world of new possibilities opens up: http://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER

+76
Nov 09 '11 at 18:42
source share

In the answer posted by Entreco, in my case there was a little tweak. I had to replace:

 if(!rect.contains((int)event.getX(), (int)event.getY())) 

for

 if(!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) 

because event.getX() and event.getY() apply only to the ImageView itself, and not to the entire screen.

+21
Jan 04
source share

I added some entries to my OnTouch and found out that I was hit by MotionEvent.ACTION_CANCEL . This is good enough for me ...

+5
May 25 '15 at 14:49
source share

The top 2 answers are good, except when the view is inside the scroll: when the scroll occurs because you move your finger, it is still registered as a touch event, but not as a MotionEvent.ACTION_MOVE event. Therefore, to improve the answer (which is only needed if your view is inside the scroll element):

 private Rect rect; // Variable rect to hold the bounds of the view public boolean onTouch(View v, MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_DOWN){ // Construct a rect of the view bounds rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); } else if(rect != null && !rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){ // User moved outside bounds } return false; } 

I tested this on Android 4.3 and Android 4.4

I did not notice any differences between Moritz's answer and top 2, but this also applies to his answer:

 private Rect rect; // Variable rect to hold the bounds of the view public boolean onTouch(View v, MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_DOWN){ // Construct a rect of the view bounds rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); } else if (rect != null){ v.getHitRect(rect); if(rect.contains( Math.round(v.getX() + event.getX()), Math.round(v.getY() + event.getY()))) { // inside } else { // outside } } return false; } 
+2
Apr 14 '15 at 8:05
source share

While the answer from @FrostRocket is correct, you should use view.getX () and Y to account for translation changes:

  view.getHitRect(viewRect); if(viewRect.contains( Math.round(view.getX() + event.getX()), Math.round(view.getY() + event.getY()))) { // inside } else { // outside } 
+1
Oct 17 '14 at 10:37
source share

Here is a View.OnTouchListener that you can use to see if MotionEvent.ACTION_UP was sent when the user had his finger outside the view:

 private OnTouchListener mOnTouchListener = new View.OnTouchListener() { private Rect rect; @Override public boolean onTouch(View v, MotionEvent event) { if (v == null) return true; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); return true; case MotionEvent.ACTION_UP: if (rect != null && !rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) { // The motion event was outside of the view, handle this as a non-click event return true; } // The view was clicked. // TODO: do stuff return true; default: return true; } } }; 
0
Jan 19 '15 at 9:04
source share
 view.setClickable(true); view.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (!v.isPressed()) { Log.e("onTouch", "Moved outside view!"); } return false; } }); 

view.isPressed uses view.pointInView and includes some touches. If you don't want slop, just copy the logic from the internal view.pointInView (which is public but hidden, so it is not part of the official API and may disappear at any time).

 view.setClickable(true); view.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { v.setTag(true); } else { boolean pointInView = event.getX() >= 0 && event.getY() >= 0 && event.getX() < (getRight() - getLeft()) && event.getY() < (getBottom() - getTop()); boolean eventInView = ((boolean) v.getTag()) && pointInView; Log.e("onTouch", String.format("Dragging currently in view? %b", pointInView)); Log.e("onTouch", String.format("Dragging always in view? %b", eventInView)); v.setTag(eventInView); } return false; } }); 
0
Oct 06 '15 at 22:17
source share

I had the same problem as the OP when I wanted to know when (1) a particular View was affected, and also (2) when when you click View or (3) when tapping down moves outside the View . I put together various answers in this thread to create a simple View.OnTouchListener extension (named SimpleTouchListener ) so that others don't have to bother with the MotionEvent object. The source for the class can be found here or at the bottom of this answer.

To use this class, simply set it as the only parameter to the View.setOnTouchListener(View.OnTouchListener) method:

 myView.setOnTouchListener(new SimpleTouchListener() { @Override public void onDownTouchAction() { // do something when the View is touched down } @Override public void onUpTouchAction() { // do something when the down touch is released on the View } @Override public void onCancelTouchAction() { // do something when the down touch is canceled // (eg because the down touch moved outside the bounds of the View } }); 

Here is the source of this class that you can add to your project:

 public abstract class SimpleTouchListener implements View.OnTouchListener { /** * Flag determining whether the down touch has stayed with the bounds of the view. */ private boolean touchStayedWithinViewBounds; @Override public boolean onTouch(View view, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: touchStayedWithinViewBounds = true; onDownTouchAction(); return true; case MotionEvent.ACTION_UP: if (touchStayedWithinViewBounds) { onUpTouchAction(); } return true; case MotionEvent.ACTION_MOVE: if (touchStayedWithinViewBounds && !isMotionEventInsideView(view, event)) { onCancelTouchAction(); touchStayedWithinViewBounds = false; } return true; case MotionEvent.ACTION_CANCEL: onCancelTouchAction(); return true; default: return false; } } /** * Method which is called when the {@link View} is touched down. */ public abstract void onDownTouchAction(); /** * Method which is called when the down touch is released on the {@link View}. */ public abstract void onUpTouchAction(); /** * Method which is called when the down touch is canceled, * eg because the down touch moved outside the bounds of the {@link View}. */ public abstract void onCancelTouchAction(); /** * Determines whether the provided {@link MotionEvent} represents a touch event * that occurred within the bounds of the provided {@link View}. * * @param view the {@link View} to which the {@link MotionEvent} has been dispatched. * @param event the {@link MotionEvent} of interest. * @return true iff the provided {@link MotionEvent} represents a touch event * that occurred within the bounds of the provided {@link View}. */ private boolean isMotionEventInsideView(View view, MotionEvent event) { Rect viewRect = new Rect( view.getLeft(), view.getTop(), view.getRight(), view.getBottom() ); return viewRect.contains( view.getLeft() + (int) event.getX(), view.getTop() + (int) event.getY() ); } } 
0
Mar 26 '17 at 16:09
source share



All Articles