Quelle est la différence entre onInterceptTouchEvent
et dispatchTouchEvent
dans Android?
Selon le guide du développeur 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 ensemble dans une hiérarchie de vues (ViewGroup
)?
Le meilleur endroit pour démystifier ceci est le code source. Les docs sont terriblement inadéquats pour expliquer cela.
dispatchTouchEvent est actuellement défini sur Activity, View et ViewGroup. Pensez-y comme un contrôleur qui décide comment acheminer les événements tactiles.
Par exemple, le cas le plus simple est celui de View.dispatchTouchEvent qui acheminera l'événement tactile vers/ OnTouchListener.onTouch s'il est défini ou avec la méthode d'extension onTouchEvent .
Pour ViewGroup.dispatchTouchEvent les choses sont bien plus compliquées. Il doit déterminer laquelle de ses vues enfants doit recevoir l'événement (en appelant child.dispatchTouchEvent). Il s'agit en gros d'un algorithme de test de hit qui permet de déterminer le rectangle englobant de la vue enfant contenant les coordonnées du point de contact.
Mais avant de pouvoir envoyer l'événement à la vue enfant appropriée, le parent peut espionner et/ou intercepter l'événement tous ensemble. C’est ce pour quoi onInterceptTouchEvent est là pour ça. Donc, cette méthode appelle d'abord cette méthode avant de tester le hit et si l'événement a été détourné (en retournant true à partir de onInterceptTouchEvent), il envoie un ACTION_CANCEL aux vues enfant afin qu'elles puissent abandonner le traitement de leur événement tactile et à partir de ce moment, tous les événements tactiles du niveau parent sont envoyés vers onTouchListener.onTouch (si défini) ou onTouchEvent (). Dans ce cas également, onInterceptTouchEvent n'est jamais appelé à nouveau.
Souhaitez-vous même remplacer le fichier [Activity | ViewGroup | View] .dispatchTouchEvent? Sauf si vous effectuez un routage personnalisé, vous ne devriez probablement pas.
Les méthodes d'extension principales sont ViewGroup.onInterceptTouchEvent si vous souhaitez espionner et/ou intercepter un événement tactile au niveau parent et View.onTouchListener/View.onTouchEvent pour la gestion des événements principaux.
Dans l’ensemble, son design trop compliqué, mais l’application Android s’appuie davantage sur la flexibilité que sur la simplicité.
Voici quelques suppléments visuels aux autres réponses. Ma réponse complète est ici .
La méthode dispatchTouchEvent()
d'une ViewGroup
utilise onInterceptTouchEvent()
pour choisir si elle doit immédiatement gérer l'événement tactile (avec onTouchEvent()
) ou continuer à notifier les méthodes dispatchTouchEvent()
de ses enfants.
Il y a beaucoup de confusion à propos de ces méthodes, mais ce n'est en réalité pas si compliqué. La plupart de la confusion est parce que:
View/ViewGroup
ou l'un de ses enfants ne retourne pas la valeur true dans onTouchEvent
, dispatchTouchEvent
et onInterceptTouchEvent
sera SEULEMENT.__déclaré MotionEvent.ACTION_DOWN
. Sans une variable true de onTouchEvent
, la vue parent supposera que votre vue n'a pas besoin de Les MotionEvents.MotionEvent.ACTION_DOWN
, même si votre ViewGroup renvoie true dans onTouchEvent
.L'ordre de traitement est comme ceci:
dispatchTouchEvent
est appelé.onInterceptTouchEvent
est appelé pour MotionEvent.ACTION_DOWN
ou lorsque L'un des enfants du groupe de vues retourne la valeur true dans onTouchEvent
.onTouchEvent
est d'abord appelé sur les enfants du groupe de vues et Quand aucun des enfants ne retourne vrai, il est appelé sur le View/ViewGroup
.Si vous souhaitez prévisualiser TouchEvents/MotionEvents
sans désactiver les événements sur vos enfants, vous devez procéder de deux manières:
dispatchTouchEvent
pour prévisualiser l'événement et renvoyer super.dispatchTouchEvent(ev)
;onTouchEvent
et renvoyez true, sinon vous n’obtiendrez aucune MotionEvent
sauf MotionEvent.ACTION_DOWN
.Si vous souhaitez détecter un geste comme un événement de balayage, sans désactiver d'autres événements sur vos enfants tant que vous n'avez pas détecté le geste, vous pouvez le faire comme suit:
onInterceptTouchEvent
lorsque votre indicateur est défini sur annuler le traitement de MotionEvent par vos enfants. C’est aussi un endroit pratique pour réinitialiser votre drapeau, car onInterceptTouchEvent ne sera plus appelé De nouveau jusqu’au prochain MotionEvent.ACTION_DOWN
.Exemple de substitution dans un FrameLayout
(mon exemple est en C # car je programme avec Xamarin Android, mais la logique est identique 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;
}
Je suis tombé sur une explication très intuitive sur cette page Web http://doandroids.com/blogs/tag/codeexample/ . Tiré de là:
- boolean onTouchEvent (MotionEvent ev) - appelé chaque fois qu'un événement tactile ayant cette vue comme cible est détecté
- boolean onInterceptTouchEvent (MotionEvent ev) - appelé chaque fois qu'un événement tactile est détecté avec ce ViewGroup ou un de ses enfants comme cible. Si cette fonction renvoie la valeur true, MotionEvent sera intercepté, ce qui signifie qu'il ne sera pas transmis à l'enfant, mais plutôt à onTouchEvent de cette vue.
dispatchTouchEvent gère avant onInterceptTouchEvent.
En utilisant 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 sera comme:
I/System.out(25900): Event - dispatchTouchEvent
I/System.out(25900): Event - onInterceptTouchEvent
Donc, si vous travaillez avec ces 2 gestionnaires, utilisez dispatchTouchEvent pour gérer en première instance l'événement, qui ira à onInterceptTouchEvent.
Une autre différence est que si dispatchTouchEvent renvoie 'false', l'événement ne sera pas propagé à l'enfant, dans ce cas, l'EditText, tandis que si vous renvoyez false dans onInterceptTouchEvent, l'événement sera toujours envoyé à l'EditText.
Vous pouvez trouver la réponse dans cette vidéo https://www.youtube.com/watch?v=SYoN-OvdZ3M&list=PLonJJ3BVjZW6CtAMbJz1XD8ELUs1KXaTD&index=19 et les 3 prochaines vidéos. Tous les événements tactiles sont très bien expliqués, c’est très clair et plein d’exemples.
Réponse courte:dispatchTouchEvent()
sera appelé first of all.
Petit conseil: ne doit pas remplacer dispatchTouchEvent()
car difficile à contrôler, cela peut parfois ralentir vos performances. IMHO, je suggère de remplacer onInterceptTouchEvent()
.
Parce que la plupart des réponses mentionnent assez clairement l’événement tactile du flux sur l’activité/la vue groupe/vue, j’ajoute plus de détails sur le code de ces méthodes dans ViewGroup
(en ignorant dispatchTouchEvent()
):
onInterceptTouchEvent()
sera appelé en premier, l'événement ACTION sera appelé respectivement vers le bas -> déplacer -> vers le haut. Il y a 2 cas:
Si vous renvoyez false dans 3 cas (ACTION_DOWN, ACTION_MOVE, ACTION_UP), il sera considéré que le parent n'aura pas besoin de cet événement tactile, donc onTouch()
des parents n'appelle jamais, mais onTouch()
des enfants appellera à la place; cependant s'il vous plaît noter:
onInterceptTouchEvent()
continue de recevoir l'événement tactile tant que ses enfants n'appellent pas requestDisallowInterceptTouchEvent(true)
. onTouch()
.Inversement, si vous renvoyez true, le parent volera immédiatement cet événement tactile et onInterceptTouchEvent()
s'arrêtera immédiatement. Au lieu de cela, onTouch()
des parents sera appelé ainsi que tous les onTouch()
des enfants recevront le dernier événement d'action - ACTION_CANCEL (ainsi, cela signifie que les parents ont volé un événement tactile et que les enfants ne pourront plus le gérer). Le flux de onInterceptTouchEvent()
return false est normal, mais il y a une petite confusion avec return true, alors je le répertorie ici:
onTouch()
des parents recevront ACTION_DOWN à nouveau et les actions suivantes (ACTION_MOVE, ACTION_UP).onTouch()
des parents recevra next ACTION_MOVE (pas le même ACTION_MOVE dans onInterceptTouchEvent()
) et les actions suivantes (ACTION_MOVE, ACTION_UP).onTouch()
des parents seront PAS appelés du tout car il est trop tard pour que les parents volent un événement tactile.Une dernière chose que important est ACTION_DOWN de l’événement dans onTouch()
déterminera si la vue souhaite recevoir plus d’actions de cet événement ou non. Si la vue retourne true à ACTION_DOWN dans onTouch()
, cela signifie que la vue est disposée à recevoir plus d'actions de cet événement. Sinon, renvoyer false à ACTION_DOWN dans onTouch()
signifiera que la vue ne recevra plus aucune action de cet événement.
Le code suivant dans une sous-classe ViewGroup empêcherait ses conteneurs parents de recevoir des événements tactiles:
@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;
}
J'ai utilisé cela avec une superposition personnalisée pour empêcher les vues en arrière-plan de répondre aux événements tactiles.
La différence principale:
• Activity.dispatchTouchEvent (MotionEvent) - Cela permet à votre activité pour intercepter tous les événements tactiles avant leur envoi au la fenêtre.
• ViewGroup.onInterceptTouchEvent (MotionEvent) - Cela permet un ViewGroup pour regarder les événements tels qu'ils sont envoyés aux vues enfants.
Le onInterceptTouchEvent()
de ViewGroup est toujours le point d'entrée de l'événement ACTION_DOWN
qui est le premier événement à se produire.
Si vous souhaitez que ViewGroup traite ce geste, retournez true à partir de onInterceptTouchEvent()
. En renvoyant true, le groupe onTouchEvent()
de ViewGroup recevra tous les événements suivants jusqu'au prochain ACTION_UP
ou ACTION_CANCEL
et, dans la plupart des cas, les événements tactiles entre ACTION_DOWN
et ACTION_UP
ou ACTION_CANCEL
sont ACTION_MOVE
. , qui seront normalement reconnus comme des gestes de défilement/défilement.
Si vous renvoyez false à partir de onInterceptTouchEvent()
, le onTouchEvent()
de la vue cible sera appelé. Il sera répété pour les messages suivants jusqu'à ce que vous renvoyiez la valeur true à partir de onInterceptTouchEvent()
.
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume =false;
if(onInterceptTouchEvent(ev){
consume = onTouchEvent(ev);
}else{
consume = child.dispatchTouchEvent(ev);
}
}
À mon avis, dispatchTouchEvent()
est une partie très importante de la manipulation des contacts sur un périphérique. Activity
, ViewGroup
, View
ont une réalisation de cette méthode. Si nous prenons en compte la méthode de ViewGroup
, nous verrons que dispatchTouchEvent()
appelle onInterceptTouchEvent()
La réponse complète SO est ici
Activity et View utilisent les méthodes dispatchTouchEvent () et onTouchEvent.Le ViewGroup utilise également ces méthodes, mais une autre méthode appelée onInterceptTouchEvent. Le type de retour de ces méthodes est booléen, vous pouvez contrôler l'itinéraire de distribution via la valeur de retour.
La répartition de l'événement dans Android commence par Activité-> ViewGroup-> View.