Wie können wir in Android erkennen, ob ein Benutzer eine Schaltfläche berührt und aus dem Bereich dieser Schaltfläche zieht?
Android: Erkennen, ob der Benutzer berührt und aus dem Schaltflächenbereich zieht?
Anticafe
Entreco
Überprüfen Sie das MotionEvent.MOVE_OUTSIDE: Überprüfen Sie das 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;
}
HINWEIS: Wenn Sie auf Android 4.0 abzielen möchten, öffnet sich eine ganze Welt neuer Möglichkeiten:
http://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER
-
Entschuldigung, aber es funktioniert nicht. Ich kann nur ACTION_UP, ACTION_DOWN und ACTION_MOVE fangen, aber nicht ACTION_OUTSIDE.
– Anticafe
10. November 2011 um 14:07 Uhr
-
Es ist ab API-Level 3 verfügbar: developer.android.com/reference/android/view/…
– Entreco
12. November 2011 um 16:02 Uhr
-
Gemäß dieser Antwort (stackoverflow.com/questions/6162497/…) glaube ich, dass wir ACTION_OUTSIDE nur verwenden können, um zu erkennen, ob sich Berührung außerhalb einer Aktivität befindet, nicht einer Ansicht.
– Anticafe
13. November 2011 um 3:11 Uhr
-
In der Antwort von FrostRocket finden Sie eine Aktualisierung des rect.contains-Tests, damit dies für einzelne Ansichten funktioniert, die nicht am Ursprung positioniert sind.
– DanJ
11. Mai 2013 um 2:26 Uhr
-
Ich glaube, wenn Sie bekommen wollen
ACTION_OUTSIDE
Sie müssen verwendengetActionMasked()
. Oder Sie könnten stattdessen die Masken vergleichen:if ((event.getAction() & MotionEvent.ACTION_OUTSIDE) == MotionEvent.ACTION_OUTSIDE)
. Das ist weilACTION_OUTSIDE
wird nie ausgelöst, ohne bereits eine andere Aktion zu haben, also wird es maskiert.– Chad Bingham
13. Mai 2017 um 4:10 Uhr
0xMatthewGroves
Die von Entreco gepostete Antwort musste in meinem Fall leicht angepasst werden. Ich musste ersetzen:
if(!rect.contains((int)event.getX(), (int)event.getY()))
zum
if(!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY()))
Weil event.getX()
und event.getY()
gelten nur für die ImageView selbst, nicht für den gesamten Bildschirm.
-
+1 danke, das ist der universellere Ansatz und funktioniert auch in benutzerdefinierten Ansichtsimplementierungen
– Knickedi
14. Juni 2014 um 20:17 Uhr
Adil Hussein
Ich hatte das gleiche Problem wie das OP, wobei ich wissen wollte, wann (1) ein bestimmtes View
aufgesetzt wurde, sowie entweder (2a) beim Loslassen der Abwärtsberührung auf der View
oder (2b) wenn sich der Down Touch außerhalb der Grenzen von bewegte View
. Ich habe die verschiedenen Antworten in diesem Thread zusammengeführt, um eine einfache Erweiterung von zu erstellen View.OnTouchListener
(genannt SimpleTouchListener
), damit sich andere nicht damit herumschlagen müssen MotionEvent
Objekt. Die Quelle für diese Klasse kann gefunden werden hier oder am Ende dieser Antwort.
Um diese Klasse zu verwenden, legen Sie sie einfach als Parameter von fest View.setOnTouchListener(View.OnTouchListener)
Methode folgt:
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
}
});
Hier ist die Quelle der Klasse, die Sie gerne zu Ihrem Projekt hinzufügen können:
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()
);
}
}
-
Dies ist eine großartige Antwort, insbesondere für Personen, die mit dem Problem arbeiten, wenn ACTION_CANCEL nicht wie vorgesehen ausgelöst wird.
– Neuer Typ
7. März 2018 um 16:27 Uhr
-
Ja, es ist eine großartige Antwort!
– Boris Karloff
8. September 2018 um 14:52 Uhr
Ich habe einige Anmeldungen in meinem OnTouch hinzugefügt und das herausgefunden MotionEvent.ACTION_CANCEL
wurde getroffen. Das reicht mir…
Bart Burg
Die oberen 2 Antworten sind in Ordnung, außer wenn sich die Ansicht in einer Bildlaufansicht befindet: Wenn das Scrollen stattfindet, weil Sie Ihren Finger bewegen, wird es immer noch als Touch-Ereignis registriert, aber nicht als MotionEvent.ACTION_MOVE-Ereignis. Um die Antwort zu verbessern (was nur erforderlich ist, wenn sich Ihre Ansicht in einem Bildlaufelement befindet):
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;
}
Ich habe dies auf Android 4.3 und Android 4.4 getestet
Ich habe keine Unterschiede zwischen der Antwort von Moritz und den Top 2 festgestellt, aber dies gilt auch für seine Antwort:
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;
}
-
Ich habe diesen Code gerade auf einer Schaltfläche implementiert, die in einem Element einer RecyclerView enthalten ist und perfekt funktioniert. Ich wollte nur sagen, dass Sie das neue Rect auf dem loswerden können ACTION_DOWN wenn Sie es vor getHitRect hinzufügen. Rect rect = neues Rect(); getHitRect(recht);
– José M. Lechon
6. März 2017 um 16:26 Uhr
Gibolt
Wiederverwendbare Kotlin-Lösung
Ich habe mit zwei benutzerdefinierten Erweiterungsfunktionen begonnen:
val MotionEvent.up get() = action == MotionEvent.ACTION_UP
fun MotionEvent.isIn(view: View): Boolean {
val rect = Rect(view.left, view.top, view.right, view.bottom)
return rect.contains((view.left + x).toInt(), (view.top + y).toInt())
}
Hören Sie dann auf Berührungen in der Ansicht. Dies wird nur ausgelöst, wenn ACTION_DOWN ursprünglich in der Ansicht war. Wenn Sie Ihren Finger loslassen, wird überprüft, ob er noch in der Ansicht war.
myView.setOnTouchListener { view, motionEvent ->
if (motionEvent.up && !motionEvent.isIn(view)) {
// Talk your action here
}
false
}
-
Ich habe diesen Code gerade auf einer Schaltfläche implementiert, die in einem Element einer RecyclerView enthalten ist und perfekt funktioniert. Ich wollte nur sagen, dass Sie das neue Rect auf dem loswerden können ACTION_DOWN wenn Sie es vor getHitRect hinzufügen. Rect rect = neues Rect(); getHitRect(recht);
– José M. Lechon
6. März 2017 um 16:26 Uhr
mpkuth
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
Verwendet view.pointInView
und enthält etwas Touch Slop. Wenn Sie keinen Slop wollen, kopieren Sie einfach die Logik aus dem internen view.pointInView
(die öffentlich, aber versteckt ist, also nicht Teil der offiziellen API ist und jederzeit verschwinden kann).
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;
}
});