Android: Erkennen, ob der Benutzer berührt und aus dem Schaltflächenbereich zieht?

Lesezeit: 7 Minuten

Benutzer-Avatar
Anticafe

Wie können wir in Android erkennen, ob ein Benutzer eine Schaltfläche berührt und aus dem Bereich dieser Schaltfläche zieht?

Benutzer-Avatar
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 verwenden getActionMasked(). Oder Sie könnten stattdessen die Masken vergleichen: if ((event.getAction() & MotionEvent.ACTION_OUTSIDE) == MotionEvent.ACTION_OUTSIDE). Das ist weil ACTION_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

Benutzer-Avatar
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

Benutzer-Avatar
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…

Benutzer-Avatar
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


Benutzer-Avatar
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


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

1175380cookie-checkAndroid: Erkennen, ob der Benutzer berührt und aus dem Schaltflächenbereich zieht?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy