275 votes

Android: Différence entre onInterceptTouchEvent et dispatchTouchEvent?

Quelle est la différence entre onInterceptTouchEvent et dispatchTouchEvent dans Android?

Selon le guide de développement Android, les deux méthodes peuvent être utilisées pour intercepter un événement tactile ( MotionEvent ), mais quelle est la différence?

Comment onInterceptTouchEvent , dispatchTouchEvent et onTouchEvent interagissent-ils ensemble dans une hiérarchie de vues ( ViewGroup )? Un court exemple serait très utile ... merci d'avance!

297voto

numan salati Points 4684

Le meilleur endroit pour démystifier c'est le code source. Les docs sont malheureusement insuffisantes à expliquer cela.

dispatchTouchEvent est réellement définie sur l'Activité, la Vue et ViewGroup. Pensez-y comme un contrôleur qui décide de la façon de route les événements tactiles.

Par exemple, le cas le plus simple est celui de la Vue.dispatchTouchEvent qui permettra d'acheminer l'événement tactile soit OnTouchListener.onTouch si elle est définie ou à l'extension de la méthode onTouchEvent.

Pour ViewGroup.dispatchTouchEvent les choses sont plus compliquées. Il doit comprendre que l'un de ses vues enfant doit obtenir de l'événement (par l'appel de l'enfant.dispatchTouchEvent). C'est fondamentalement un coup algorithme de dépistage où vous comprendre quel enfant de voir du rectangle de délimitation contient les coordonnées du point de contact.

Mais avant de pouvoir l'envoi de l'événement appropriée en vue d'enfant, le parent peut espionner et/ou d'intercepter l'événement tous ensemble. C'est ce que onInterceptTouchEvent est là pour. Donc il appelle cette méthode avant de faire le coup d'essai et si l'événement a été détourné (en retournant vrai onInterceptTouchEvent) il envoie un ACTION_CANCEL à l'enfant vues, de sorte qu'ils peuvent abandonner leur touche de traitement des événements (par rapport à des événements tactiles) et à partir de là, tous les événements tactiles au niveau du parent sont expédiés à onTouchListener.onTouch (si défini) ou onTouchEvent(). Aussi, dans ce cas, onInterceptTouchEvent n'est jamais appelé de nouveau.

Voulez-vous même de vouloir remplacer [Activité|ViewGroup|View].dispatchTouchEvent? Sauf si vous faites partie de routage personnalisé, vous ne devriez probablement pas.

Les principales méthodes d'extension sont ViewGroup.onInterceptTouchEvent si vous voulez espionner et/ou d'interception d'événement tactile au niveau du parent et de la Vue.onTouchListener/Vue.onTouchEvent pour l'événement principal de la manipulation.

Dans l'ensemble de ses trop compliqué conception de l'omi mais android api pencher davantage vers de la flexibilité de la simplicité.

264voto

seb Points 804

Parce que c'est le premier résultat sur Google. Je veux partager avec vous un grand discours par Dave Smith sur Youtube: la maîtrise de l'Tactile Androïde de Système et les slides sont disponibles ici. Il m'a donné une bonne compréhension profonde sur le Tactile Androïde de Système:

Comment l' Activité des poignées de contact:

  • Activity.dispatchTouchEvent()
    • Toujours le premier à être appelé
    • Envoie de vue de la racine de joint de Fenêtre
    • onTouchEvent()
      • Appelé si aucun point de vue n'consommer de l'événement
      • Toujours le dernier à être appelé

Comment la Vue des poignées de contact:

  • View.dispatchTouchEvent()
    • Envoie à l'auditeur d'abord, s'il existe
      • View.OnTouchListener.onTouch()
    • Si non consommé, processus de le toucher lui-même
      • View.onTouchEvent()

Comment un ViewGroup poignées de contact:

  • ViewGroup.dispatchTouchEvent()
    • onInterceptTouchEvent()
      • Vérifiez si il devrait supplanter les enfants
      • Passes ACTION_CANCEL d'enfants actifs
      • Retour sur le cas une fois en consomme tous les événements subséquents
    • Pour chaque enfant, dans l'ordre inverse où elles ont été ajoutées
      • Si le toucher est pertinente (vue de l'intérieur), child.dispatchTouchEvent()
      • Si ce n'est pas manipulé par les précédents, l'envoi à la vue suivante
    • Si aucun des enfants de la poignée de l'événement, l'auditeur obtient une chance
      • OnTouchListener.onTouch()
    • Si pas d'auditeur, ou n'est pas manipulé
      • onTouchEvent()
  • Interception des événements de sauter par-dessus l'enfant étape

Il fournit également un exemple de code de touche personnalisée sur github.com/devunwired/.

Réponse: Fondamentalement, l' dispatchTouchEvent() est appelée sur chaque View de la couche de déterminer si un View est intéressé au cours d'un même geste. En ViewGroup le ViewGroup a la capacité de voler les événements tactiles dans ses dispatchTouchEvent()-méthode, avant qu'il appellerait dispatchTouchEvent() sur les enfants. L' ViewGroup ne cesser l'envoi si l' ViewGroup onInterceptTouchEvent()-méthode renvoie la valeur true. La différence est qu' dispatchTouchEvent()distribue MotionEvents et onInterceptTouchEvent indique s'il doit intercepter (pas d'envoi de l' MotionEvent d'enfants) ou non (dispatching pour les enfants).

Vous pouvez imaginer le code de ViewGroup faire plus ou moins ce (très simplifié):

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

23voto

Marcel W Points 936

Il y a beaucoup de confusion au sujet de ces méthodes, mais il est en fait pas si compliqué que ça. La plupart de la confusion, parce que:

  1. Si votre vue(groupe) ou un de ses enfants, de ne pas retourner en vrai onTouchEvent, dispatchTouchEvent et onInterceptTouchEvent ne être appelé pour MotionEvent.ACTION_DOWN. Sans un vrai onTouchEvent, la vue parent assume que votre vue n'a pas besoin de le MotionEvents.
  2. Lorsque aucun des enfants d'un ViewGroup retourne true en onTouchEvent, onInterceptTouchEvent ne sera appelée pour MotionEvent.ACTION_DOWN, même si votre ViewGroup retourne true en onTouchEvent.

Ordre de traitement est comme ceci:

  1. dispatchTouchEvent est appelé.
  2. onInterceptTouchEvent est appelé pour MotionEvent.ACTION_DOWN ou lorsque l'un des enfants de la ViewGroup renvoyé true dans onTouchEvent.
  3. onTouchEvent est d'abord appelée sur les enfants de la ViewGroup et lorsque aucun des enfants de retour de la vraie il est appelé sur le Affichage(Groupe).

Si vous souhaitez afficher un aperçu TouchEvents/MotionEvents sans désactiver les événements de vos enfants, vous devez faire deux choses:

  1. Remplacer dispatchTouchEvent pour un aperçu de la manifestation et retour super.dispatchTouchEvent(ev);
  2. Remplacer onTouchEvent et renvoie true, sinon vous n'aurez pas de MotionEvent sauf MotionEvent.ACTION_DOWN.

Si vous voulez détecter un geste comme un balayage de l'événement, sans désactiver d'autres événements de votre enfant aussi longtemps que vous n'avez pas de détecter le geste, vous pouvez le faire comme ceci:

  1. Aperçu de la MotionEvents comme décrit ci-dessus et de définir un indicateur lorsque vous détecté que votre geste.
  2. Retourne true en onInterceptTouchEvent lors de votre drapeau est réglé sur annuler MotionEvent traitement par vos enfants. C'est aussi une pratique place pour réinitialiser votre drapeau, parce que onInterceptTouchEvent ne sera pas appelé de nouveau jusqu'à la prochaine MotionEvent.ACTION_DOWN.

Exemple des remplacements dans un FrameLayout (mon exemple en C# comme je suis à la programmation avec Xamarin Android, mais la logique est la même en Java):

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

9voto

Krzysztow Points 313

Je l'ai trouvé très intuitif explication à cette page web http://doandroids.com/blogs/tag/codeexample/. Prises à partir de là:

  • boolean onTouchEvent(MotionEvent ev) - appelé à chaque fois qu'un événement tactile avec ce point de Vue que la cible est détectée
  • boolean onInterceptTouchEvent(MotionEvent ev) - appelé à chaque fois qu'une touche est détectée avec cette ViewGroup ou un enfant comme cible. Si cette fonction renvoie true, le MotionEvent sera intercepté ce qui signifie qu'il sera pas être transmis à l'enfant, mais plutôt à la onTouchEvent de ce point de Vue.

9voto

Dayerman Points 1376

dispatchTouchEvent poignées avant onInterceptTouchEvent.

À l'aide de cet exemple simple:

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

Vous pouvez voir que le journal willl:

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

Ainsi, dans le cas où vous travaillez avec ces 2 gestionnaires utilisent dispatchTouchEvent à manipuler sur la première occurrence de l'événement, qui iront à onInterceptTouchEvent.

Une autre différence est que si dispatchTouchEvent renvoie 'false' l'événement ne se propage à l'enfant, dans ce cas, l'EditText, alors que si l'on retourne la valeur false dans onInterceptTouchEvent l'événement encore obtenir l'expédition à l'EditText

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X