In Android, how can we detect if a user touches on button and drags out of region of this button?
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 touch slop. If you don't want slop, just copy the logic from the internal view.pointInView
(which is public, but hidden so it's not a part of the official API and could 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;
}
});
I added some logging in my OnTouch and found out that MotionEvent.ACTION_CANCEL
was being hit. That's good enough for me...
The top 2 answers are fine except when the view is inside a scrollview: when scrolling takes place because you move your finger, it is still registered as a touch event but not as a MotionEvent.ACTION_MOVE event. So to improve the answer (which is only needed if your view is inside a 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's 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 haven't noticed any differences between Moritz's answer and to top 2 but this also applies for 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's 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;
}