web-dev-qa-db-de.com

Android: Unterschied zwischen onInterceptTouchEvent und dispatchTouchEvent?

Was ist der Unterschied zwischen onInterceptTouchEvent und dispatchTouchEvent in Android?

Laut Android-Entwicklerhandbuch können beide Methoden zum Abfangen eines Berührungsereignisses (MotionEvent) verwendet werden. Aber was ist der Unterschied?

Wie interagieren onInterceptTouchEvent, dispatchTouchEvent und onTouchEvent innerhalb einer Hierarchie von Views (ViewGroup) zusammen?

223
Anne Droid

Der beste Ort, um dies zu entmystifizieren, ist der Quellcode. Die Dokumente sind kläglich genug, um dies zu erklären.

dispatchTouchEvent ist eigentlich in Activity, View und ViewGroup definiert. Betrachten Sie es als Controller, der entscheidet, wie die Berührungsereignisse geleitet werden.  

Der einfachste Fall ist beispielsweise der von View.dispatchTouchEvent , der das Berührungsereignis entweder zu OnTouchListener.onTouch leitet, wenn es definiert ist, oder zur Erweiterungsmethode onTouchEvent .

Für ViewGroup.dispatchTouchEvent Dinge sind viel komplizierter. Es muss herausgefunden werden, welche der untergeordneten Ansichten das Ereignis erhalten soll (durch Aufrufen von child.dispatchTouchEvent). Hierbei handelt es sich im Wesentlichen um einen Hit-Test-Algorithmus, bei dem Sie herausfinden, welches Begrenzungsrechteck der untergeordneten Ansicht die Kontaktpunkt-Koordinaten enthält.

Bevor das Ereignis jedoch an die entsprechende untergeordnete Ansicht gesendet werden kann, können die Eltern das Ereignis gemeinsam ausspähen und/oder abfangen. Dafür gibt es onInterceptTouchEvent . Daher ruft sie diese Methode zuerst auf, bevor der Treffertest ausgeführt wird. Wenn das Ereignis entführt wurde (durch Rückgabe von true von onInterceptTouchEvent), sendet es ein ACTION_CANCEL an die untergeordneten Ansichten, damit diese ihre Berührungsereignisverarbeitung (von vorherigen Berührungsereignissen) aufgeben können und von da an werden alle Berührungsereignisse auf der übergeordneten Ebene an onTouchListener.onTouch (falls definiert) oder onTouchEvent () gesendet. OnInterceptTouchEvent wird auch in diesem Fall nie mehr aufgerufen.

Möchten Sie [Activity | ViewGroup | View] .dispatchTouchEvent überhaupt überschreiben? Wenn Sie kein benutzerdefiniertes Routing durchführen, sollten Sie dies wahrscheinlich nicht tun. 

Die wichtigsten Erweiterungsmethoden sind ViewGroup.onInterceptTouchEvent, wenn Sie das Berührungsereignis auf übergeordneter Ebene ausspionieren und/oder abfangen möchten, und View.onTouchListener/View.onTouchEvent für die Behandlung von Hauptereignissen.

Alles in allem ist sein übermäßig kompliziertes Design imo, aber Android-Apps neigen eher zu Flexibilität als zu Einfachheit.

255
numan salati

Weil dies das erste Ergebnis bei Google ist. Ich möchte mit Ihnen einen tollen Vortrag von Dave Smith über Youtube: Mastering the Android Touch System und die Folien sind verfügbar hier . Es gab mir ein gutes tiefes Verständnis über das Android Touch System:

Wie die Aktivität mit Berührung umgeht:

  • Activity.dispatchTouchEvent()
    • Immer zuerst angerufen werden
    • Sendet das Ereignis an die Stammansicht, die an Window angehängt ist
    • onTouchEvent()
      • Wird aufgerufen, wenn keine Aufrufe das Ereignis verbrauchen
      • Immer zuletzt angerufen werden

Wie die Ansicht mit Berührung umgeht:

  • View.dispatchTouchEvent()
    • Sendet das Ereignis zuerst an den Listener, falls vorhanden
      • View.OnTouchListener.onTouch()
    • Wird es nicht konsumiert, verarbeitet es die Berührung selbst
      • View.onTouchEvent()

Wie eine ViewGroup mit Berührungen umgeht:

  • ViewGroup.dispatchTouchEvent()
    • onInterceptTouchEvent()
      • Überprüfen Sie, ob es Kinder ersetzen soll
      • Übergibt ACTION_CANCEL An das aktive Kind
      • Return true einmalig, verbraucht alle nachfolgenden Ereignisse
    • Für jede untergeordnete Ansicht wurden sie in umgekehrter Reihenfolge hinzugefügt
      • Wenn Berührung relevant ist (Innenansicht), child.dispatchTouchEvent()
      • Wenn nicht durch vorherige behandelt, Versand zur nächsten Ansicht
    • Wenn keine Kinder mit dem Ereignis umgehen, hat der Zuhörer eine Chance
      • OnTouchListener.onTouch()
    • Wenn kein Zuhörer oder nicht behandelt
      • onTouchEvent()
  • Abgefangene Ereignisse springen über untergeordnete Schritte

Er bietet auch einen Beispielcode für benutzerdefinierte Berührungen unter github.com/devunwired/ .

Antwort: Grundsätzlich wird die Funktion dispatchTouchEvent() auf jeder Ebene View aufgerufen, um festzustellen, ob eine View ist an einer andauernden Geste interessiert. In einem ViewGroup kann der ViewGroup die Berührungsereignisse in seiner dispatchTouchEvent()- Methode stehlen, bevor er dispatchTouchEvent() für die Kinder aufruft. Die ViewGroup würde den Versand nur dann stoppen, wenn die ViewGrouponInterceptTouchEvent()- Methode true zurückgibt. Der Unterschied ist, dass dispatchTouchEvent()MotionEvents sendet und onInterceptTouchEvent sagt, ob es abfangen soll (MotionEvent nicht an Kinder versenden) oder nicht (an Kinder versenden) .

Sie können sich vorstellen, dass Code einer ViewGroup ungefähr so ​​funktioniert (sehr vereinfacht):

public boolean dispatchTouchEvent(MotionEvent ev) {
    if(!onInterceptTouchEvent()){
        for(View child : children){
            if(child.dispatchTouchEvent(ev))
                return true;
        }
    }
    return super.dispatchTouchEvent(ev);
}
231
seb

Ergänzende Antwort

Hier sind einige visuelle Ergänzungen zu den anderen Antworten. Meine vollständige Antwort ist hier .

 enter image description here

 enter image description here

Die dispatchTouchEvent()-Methode einer ViewGroup verwendet onInterceptTouchEvent(), um zu entscheiden, ob sie das Berührungsereignis sofort behandeln soll (mit onTouchEvent()) oder die dispatchTouchEvent()-Methoden seiner Kinder weiterhin benachrichtigen soll.

42
Suragch

Es gibt viel Verwirrung über diese Methoden, aber es ist eigentlich nicht so kompliziert. Die meiste Verwirrung ist, weil:

  1. Wenn Ihr View/ViewGroup oder eines seiner untergeordneten Elemente in onTouchEvent nicht als wahr zurückgegeben wird, werden NUR dispatchTouchEvent und onInterceptTouchEventBE für MotionEvent.ACTION_DOWN aufgerufen. Ohne ein Wahr von onTouchEvent geht die übergeordnete Ansicht davon aus, dass Ihre Ansicht MotionEvents nicht benötigt wird.
  2. Wenn keines der untergeordneten Elemente einer ViewGroup in onTouchEvent den Wert true zurückgibt, wird onInterceptTouchEvent NUR für MotionEvent.ACTION_DOWN aufgerufen, auch wenn Ihre ViewGroup in onTouchEvent true zurückgibt.

Bearbeitungsreihenfolge ist wie folgt:

  1. dispatchTouchEvent wird aufgerufen.
  2. onInterceptTouchEvent wird für MotionEvent.ACTION_DOWN aufgerufen, oder wenn Eines der untergeordneten Elemente der ViewGroup in onTouchEvent den Wert true zurückgegeben hat.
  3. onTouchEvent wird zuerst für die Kinder der ViewGroup aufgerufen und Wenn keines der Kinder wahr zurückgibt, wird es für View/ViewGroup aufgerufen.

Wenn Sie eine Vorschau von TouchEvents/MotionEvents anzeigen möchten, ohne die Ereignisse für Ihre Kinder zu deaktivieren, müssen Sie zwei Dinge tun:

  1. Überschreiben Sie dispatchTouchEvent, um eine Vorschau des Ereignisses anzuzeigen und zurückzukehren super.dispatchTouchEvent(ev);
  2. Überschreiben Sie onTouchEvent und geben Sie true zurück. Andernfalls erhalten Sie keine MotionEvent-Werte außer MotionEvent.ACTION_DOWN.

Wenn Sie eine Geste wie ein Wischereignis erkennen möchten, ohne andere Ereignisse für Ihre Kinder zu deaktivieren, solange Sie die Geste nicht erkannt haben, können Sie dies folgendermaßen tun:

  1. Zeigen Sie die MotionEvents wie oben beschrieben in der Vorschau an und setzen Sie ein Flag, wenn Ihre Geste erkannt hat.
  2. Geben Sie in onInterceptTouchEvent den Wert true zurück, wenn Ihr Flag auf Abbrechen gesetzt ist MotionEvent-Verarbeitung durch Ihre Kinder. Dies ist auch ein bequemerplace zum Zurücksetzen Ihres Flags, da onInterceptTouchEvent nicht bis zum nächsten MotionEvent.ACTION_DOWN erneut aufgerufen wird

Beispiel für Überschreibungen in einem FrameLayout (mein Beispiel in C # ist, da ich mit Xamarin Android programmiere, aber die Logik ist in Java die gleiche):

public override bool DispatchTouchEvent(MotionEvent e)
{
    // Preview the touch event to detect a swipe:
    switch (e.ActionMasked)
    {
        case MotionEventActions.Down:
            _processingSwipe = false;
            _touchStartPosition = e.RawX;
            break;
        case MotionEventActions.Move:
            if (!_processingSwipe)
            {
                float move = e.RawX - _touchStartPosition;
                if (move >= _swipeSize)
                {
                    _processingSwipe = true;
                    _cancelChildren = true;
                    ProcessSwipe();
                }
            }
            break;
    }
    return base.DispatchTouchEvent(e);
}

public override bool OnTouchEvent(MotionEvent e)
{
    // To make sure to receive touch events, tell parent we are handling them:
    return true;
}

public override bool OnInterceptTouchEvent(MotionEvent e)
{
    // Cancel all children when processing a swipe:
    if (_cancelChildren)
    {
        // Reset cancel flag here, as OnInterceptTouchEvent won't be called until the next MotionEventActions.Down:
        _cancelChildren = false;
        return true;
    }
    return false;
}
16
Marcel W

Ich habe auf dieser Webseite http://doandroids.com/blogs/tag/codeexample/ eine sehr intuitive Erklärung erhalten. Von dort genommen:

  • boolean onTouchEvent (MotionEvent ev) - wird aufgerufen, wenn ein Berührungsereignis mit dieser Ansicht als Ziel erkannt wird
  • boolean onInterceptTouchEvent (MotionEvent ev) - wird aufgerufen, wenn ein Touch-Ereignis mit dieser ViewGroup oder einem untergeordneten Element davon als Ziel erkannt wird. Wenn diese Funktion true zurückgibt, wird das MotionEvent abgefangen, dh es wird nicht an das untergeordnete Objekt, sondern an das onTouchEvent dieser Ansicht weitergeleitet.
8
Krzysztow

dispatchTouchEvent behandelt vor onInterceptTouchEvent.

Verwenden Sie dieses einfache Beispiel:

   main = new LinearLayout(this){
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            System.out.println("Event - onInterceptTouchEvent");
            return super.onInterceptTouchEvent(ev);
            //return false; //event get propagated
        }
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            System.out.println("Event - dispatchTouchEvent");
            return super.dispatchTouchEvent(ev);
            //return false; //event DONT get propagated
        }
    };

    main.setBackgroundColor(Color.GRAY);
    main.setLayoutParams(new LinearLayout.LayoutParams(320,480));    


    viewA = new EditText(this);
    viewA.setBackgroundColor(Color.YELLOW);
    viewA.setTextColor(Color.BLACK);
    viewA.setTextSize(16);
    viewA.setLayoutParams(new LinearLayout.LayoutParams(320,80));
    main.addView(viewA);

    setContentView(main);

Sie können sehen, dass das Log so aussieht:

I/System.out(25900): Event - dispatchTouchEvent
I/System.out(25900): Event - onInterceptTouchEvent

Wenn Sie also mit diesen beiden Handlern arbeiten, verwenden Sie dispatchTouchEvent, um das Ereignis in der ersten Instanz zu behandeln, das zu onInterceptTouchEvent wechselt.

Ein weiterer Unterschied besteht darin, dass bei der Ausgabe von dispatchTouchEvent 'false' das Ereignis nicht an das untergeordnete Objekt weitergegeben wird, in diesem Fall EditText. Wenn Sie jedoch in onInterceptTouchEvent den Wert false zurückgeben, wird das Ereignis immer noch an den EditText gesendet

8
Dayerman

Die Antwort finden Sie in diesem Video https://www.youtube.com/watch?v=SYoN-OvdZ3M&list=PLonJJ3BVjZW6CtAMbJz1XD8ELUs1KXaTD&index=19 und den nächsten 3 Videos. Alle Touch-Events werden sehr gut erklärt, es ist sehr klar und voller Beispiele.

4
BlueMango

Kurze Antwort: dispatchTouchEvent() wird an erster Stelle von allen aufgerufen.

Kurzer Hinweis: sollte dispatchTouchEvent() nicht überschreiben, da es schwer zu kontrollieren ist. Manchmal kann dies die Leistung beeinträchtigen. IMHO schlage ich vor, onInterceptTouchEvent() zu überschreiben.

Da die meisten Antworten das Flow-Touch-Ereignis für Aktivität/Ansichtsgruppeansicht ziemlich eindeutig erwähnen, füge ich in ViewGroup nur weitere Details zum Code dieser Methoden hinzu (ignoriert dispatchTouchEvent()):

/ - onInterceptTouchEvent()wird zuerst aufgerufen, das ACTION-Ereignis wird aufgerufen bzw. abwärts -> bewegen -> aufwärts. Es gibt zwei Fälle:

  1. Wenn Sie false in 3 Fällen zurückgeben (ACTION_DOWN, ACTION_MOVE, ACTION_UP), wird dies in Betracht gezogen, da der Elternteil dieses Berührungsereignis nicht benötigt , alsoonTouch() der Eltern niemals , aber ruftonTouch() der Kinder wird stattdessen anrufen ; Bitte beachten Sie jedoch: 

    • Der onInterceptTouchEvent() empfängt weiterhin ein Berührungsereignis, solange seine Kinder nicht requestDisallowInterceptTouchEvent(true) aufrufen. 
    • Wenn keine Kinder dieses Ereignis erhalten (dies kann in zwei Fällen der Fall sein: es gibt keine Kinder an der Position, die der Benutzer berührt, oder gibt es Kinder, die jedoch bei ACTION_DOWN falsch sind), senden die Eltern dieses Ereignis an onTouch() der Eltern zurück.
  2. Umgekehrt, wenn Sie true zurückgeben , stiehlt der Elternteil dieses Berührungsereignis sofort und onInterceptTouchEvent() stoppt sofort, stattdessen werdenonTouch() der Eltern sowie alleonTouch() aufgerufen. von Kindern erhalten das letzte Aktionsereignis - ACTION_CANCEL (dies bedeutet, dass die Eltern das Berührungsereignis gestohlen haben, und Kinder können es von da an nicht mehr handhaben). Der Ablauf von onInterceptTouchEvent() return false ist normal, aber es gibt ein wenig Verwirrung mit return true case, daher liste ich es hier auf:

    • Bei ACTION_DOWN true zurückgeben, onTouch() der Eltern erhalten ACTION_DOWN again und folgende Aktionen (ACTION_MOVE, ACTION_UP).
    • Bei ACTION_MOVE true zurückgeben, erhalten onTouch() der Eltern next ACTION_MOVE (nicht dasselbe ACTION_MOVE in onInterceptTouchEvent()) und die folgenden Aktionen (ACTION_MOVE, ACTION_UP).
    • Bei ACTION_UP true zurückgeben, wird onTouch() der ElternNICHTüberhaupt aufgerufen, da es für die Eltern zu spät ist, das Berührungsereignis zu stehlen.

Eine weitere Sache wichtig ist: ACTION_DOWN des Ereignisses in onTouch() bestimmt, ob die Ansicht mehr Aktion von diesem Ereignis erhalten möchte oder nicht. Wenn die Ansicht bei ACTION_DOWN in onTouch() true zurückgibt, bedeutet dies, dass die Ansicht bereit ist, weitere Aktionen von diesem Ereignis zu erhalten. Andernfalls wird bei ACTION_DOWN in onTouch() der Wert false zurückgegeben. Dies bedeutet, dass die Ansicht keine weitere Aktion von diesem Ereignis erhält.

3
Nguyen Tan Dat

Der folgende Code in einer ViewGroup-Unterklasse verhindert, dass die übergeordneten Container Berührungsereignisse empfangen:

  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
    // Normal event dispatch to this container's children, ignore the return value
    super.dispatchTouchEvent(ev);

    // Always consume the event so it is not dispatched further up the chain
    return true;
  }

Ich habe dies mit einer benutzerdefinierten Überlagerung verwendet, um zu verhindern, dass Hintergrundansichten auf Berührungsereignisse reagieren.

3
James Wald

Der Hauptunterschied : 

• Activity.dispatchTouchEvent (MotionEvent) - Dies ermöglicht Ihre Aktivität um alle Berührungsereignisse abzufangen, bevor sie an die .__ gesendet werden. Fenster. 
• ViewGroup.onInterceptTouchEvent (MotionEvent) - Dies ermöglicht eine ViewGroup, um Ereignisse zu überwachen, wenn sie an untergeordnete Ansichten gesendet werden.

1
ChapMic

ViewGroups onInterceptTouchEvent() ist immer der Eintrittspunkt für das ACTION_DOWN -Ereignis, das das erste Ereignis ist. 

Wenn ViewGroup diese Geste verarbeiten soll, geben Sie true von onInterceptTouchEvent()..__ zurück. Wenn true zurückgegeben wird, erhält onTouchEvent() von ViewGroup alle nachfolgenden Ereignisse bis zum nächsten ACTION_UP oder ACTION_CANCEL und in den meisten Fällen den Berührungsereignissen zwischen ACTION_DOWN und ACTION_UP oder ACTION_CANCEL sind ACTION_MOVE, die normalerweise als Scroll-/Fling-Gesten erkannt werden.

Wenn Sie false von onInterceptTouchEvent() zurückgeben, wird die onTouchEvent() der Zielansicht aufgerufen. Es wird für nachfolgende Nachrichten wiederholt, bis Sie von onInterceptTouchEvent() als wahr zurückgegeben werden.

Quelle: http://neevek.net/posts/2013/10/13/implementing-onInterceptTouchEvent-and-onTouchEvent-for-ViewGroup.html

1
vipsy
public boolean dispatchTouchEvent(MotionEvent ev){
    boolean consume =false;
    if(onInterceptTouchEvent(ev){
        consume = onTouchEvent(ev);
    }else{
        consume = child.dispatchTouchEvent(ev);
    }
}
0
Mc_fool_himself

Meiner Meinung nach ist dispatchTouchEvent() ein sehr wichtiger Teil beim Umgang mit Berührungen auf einem Gerät. Activity, ViewGroup, View haben eine Realisierung dieser Methode. Wenn wir die Methode von ViewGroup berücksichtigen, werden wir sehen, dass dispatchTouchEvent()onInterceptTouchEvent() aufruft.

enter image description here

Die vollständige SO Antwort lautet hier

0
yoAlex5

Sowohl Activity als auch View haben die Methode dispatchTouchEvent () und onTouchEvent. Die ViewGroup hat auch diese Methoden, jedoch eine andere Methode, die onInterceptTouchEvent heißt. Der Rückgabetyp dieser Methoden ist boolean. Sie können die Versandroute über den Rückgabewert steuern.

Der Event-Versand in Android beginnt mit Activity-> ViewGroup-> View.

0
Ryan