В Android, как мы можем обнаружить, если пользователь коснулся кнопки и вытащил из области этой кнопки?
Android: обнаружение, если пользователь прикасается и вытаскивает из области кнопки?
Ответ 1
Проверьте 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 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
Ответ 2
В ответе, опубликованном Энтреко, в моем случае была небольшая настройка. Я должен был заменить:
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, а не ко всему экрану.
Ответ 3
Я добавил некоторые записи в свой OnTouch и выяснил, что был поражен MotionEvent.ACTION_CANCEL
. Это достаточно хорошо для меня...
Ответ 4
Лучшие 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 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 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;
}
Ответ 5
В то время как ответ от @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
}
Ответ 6
Вот 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;
}
}
};
Ответ 7
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, просто скопируйте логику из внутреннего 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;
}
});
Ответ 8
У меня была та же проблема, что и OP, когда мне хотелось знать, когда (1) был затронут какой-то конкретный View
, а также (2), когда при нажатии View
или (3) когда касание вниз перемещается за пределы View
. Я собрал различные ответы в этом потоке, чтобы создать простое расширение 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
// (e.g. 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,
* e.g. 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()
);
}
}