Android: обнаружение, если пользователь коснется и перетащит из области кнопки?

В Android, как мы можем определить, касается ли пользователь кнопки на кнопке и вытягивает из области этой кнопки?

Solutions Collecting From Web of "Android: обнаружение, если пользователь коснется и перетащит из области кнопки?"

Проверьте MotionEvent.MOVE_OUTSIDE: проверьте 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's 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; } 

ПРИМЕЧАНИЕ. Если вы хотите настроить таргетинг на Android 4.0, открывается целый мир новых возможностей: http://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER

Ответ, высказанный Entreco, нуждался в некоторой небольшой настройке в моем случае. Я должен был заменить:

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

для

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

Потому что event.getX() и event.getY() применяются только к самому ImageView, а не ко всему экрану.

Я добавил некоторые записи в свой OnTouch и узнал, что MotionEvent.ACTION_CANCEL попал. Это для меня достаточно …

Лучшие 2 ответа хороши, за исключением случаев, когда представление находится внутри прокрутки: когда прокрутка происходит, потому что вы перемещаете палец, она по-прежнему регистрируется как событие касания, но не как событие MotionEvent.ACTION_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'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; } 

Я тестировал это на Android 4.3 и Android 4.4

Я не заметил никаких различий между ответом Морица и топ-2, но это также относится к его ответу:

 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; } 

Хотя ответ от @FrostRocket верен, вы должны использовать view.getX () и Y для учета изменений переводов:

  view.getHitRect(viewRect); if(viewRect.contains( Math.round(view.getX() + event.getX()), Math.round(view.getY() + event.getY()))) { // inside } else { // outside } 

Вот View.OnTouchListener который вы можете использовать, чтобы увидеть, был ли отправлен MotionEvent.ACTION_UP когда пользователь имел свой палец за пределами представления:

 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; } } }; 
 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 использует view.pointInView и включает в себя некоторый сенсорный slop. Если вы не хотите slop, просто скопируйте логику из внутреннего view.pointInView (который является общедоступным, но скрытым, поэтому он не является частью официального API и может исчезнуть в любое время).

 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; } }); 

У меня была та же проблема, что и у ОП, в которой я хотел знать, когда (1) был затронут какой-то конкретный View , а также (2), когда нажатие кнопки «Вниз» было выпущено на View « View или (3), когда нажатие касалось Границ представления. Я собрал различные ответы в этом потоке, чтобы создать простое расширение View.OnTouchListener (с именем SimpleTouchListener ), чтобы другим не приходилось возиться с объектом MotionEvent . Источник для класса можно найти здесь или внизу этого ответа.

Чтобы использовать этот класс, просто установите его как единственный параметр метода View.setOnTouchListener(View.OnTouchListener) :

 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 } }); 

Вот источник этого класса, который вы можете добавить в свой проект:

 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() ); } }