Introduction
Voilà maintenant un petit moment que je souhaitais parler de la gestion de ce que j'appelle les TouchEvent
sous Android. Les TouchEvent
sont les actions utilisateur générées lorsque ce dernier interagit avec l'écran. Le framework Android propose plusieurs façon de gérer ces évenements :
Hériter de la classe
View
: il est ainsi possible de redéfinir la méthodeonTouchEvent(MotionEvent event)
appelée par le système lors d'unTouchEvent
Utiliser la notion de
Listener
: if suffit d'implémenter l'interfaceView.OnTouchListener
et de s'inscrire auprès de la vue sur laquelle on souhaite suivre les évènements par un simplesetOnTouchListener(OnTouchListener listener)
Hériter de la classe
Activity
: cette classe comporte une méthodeonTouchEvent(MotionEvent event)
qui est appelée si aucune des vues présente dans l'activité n'a consommé l'évènement. C'est en quelque sorte le dernier moyen de récupérer un évènement avant qu'il ne soit tout simplement perdu
Le but de ce post n'est pas d'expliquer comment gérer les TouchEvent
s dans votre application mais plutôt de comprendre comment Android transporte l'événement dans votre arborescence de View
s. Si vous souhaitez simplement étudier les TouchEvent
et leur utilisation dans vos vues, je vous conseille de lire la Javadoc associée à MotionEvent
. Pour résumer, il suffit de regarder la valeur de event.getAction()
pour savoir ce que l'utilisateur vient de faire. Les valeurs possibles sont :
MotionEvent.ACTION_DOWN : L'utilisateur vient d'appuyer sur l'écran. C'est la première valeur récupérée suite à une action sur l'écran
MotionEvent.ACTION_MOVE : Fait suite à l'événement précédent et indique que l'utilisateur n'a pas relaché la pression sur l'écran et est en train de bouger
MotionEvent.ACTION_UP : Envoyé lorsque l'utilisateur cesse d'appuyer sur l'écran
MotionEvent.ACTION_CANCEL : Action un peu spéciale dont je parlerai dans la suite de cet article
Préparation du cadre
Elaborons tout d'abord le cadre de notre étude : nous disposons d'une activité affichant à l'écran l'interface partiellement donnée ci-dessous. Cette dernière est composée d'un FrameLayout
affichant 3 carrés empilés les uns sur les autres :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
Le code source de l'activité principale est assez succinct et utilise la technique des listeners pour récupérer les TouchEvent
s :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
|
Le code complet de ce programme est disponible dans le zip accessible à cette adresse. Je vous conseille vivement de lancer vous même ce programme car il vous permettra de tester de façon plus approfondie et interactive les points que nous traiterons ci-dessous. Une fois lancé, vous devriez obtenir une interface comme suit :
Comportement d'Android face aux TouchEvent
s
Nous voilà près à analyser la façon dont Android gère et fait naviguer l'information dans l'arborescence des vues. Nous parlions en introduction de “consommer les évènements”. Pour informer Android qu'un évènement à été consommé ou non par une vue, il faut gérer le retour de la méthode s'occupant des TouchEvent
. Dans le cas de l'exemple, on utilise la méthode onTouch(MotionEvent event)
et on gère le code de retour (true
ou false
) en fonction de l'état des ToggleButton
.
En laissant inactif l'ensemble des vues (ToggleButton
s éteints), puis en effectuant un TouchEvent
, la sortie de débogage affiche une séquence concordant avec le modèle suivant :
05-05 16:52:23.225: DEBUG/TouchActivity(723): mZ1: ACTION_DOWN
05-05 16:52:23.225: DEBUG/TouchActivity(723): mZ2: ACTION_DOWN
05-05 16:52:23.245: DEBUG/TouchActivity(723): mFrameLayout: ACTION_DOWN
05-05 16:52:23.255: DEBUG/TouchActivity(723): Activity: ACTION_DOWN
05-05 16:52:24.264: DEBUG/TouchActivity(723): Activity: ACTION_MOVE
05-05 16:52:24.305: DEBUG/TouchActivity(723): Activity: ACTION_MOVE
05-05 16:52:25.150: DEBUG/TouchActivity(723): Activity: ACTION_UP
Ce premier test nous permet d'en savoir plus sur la gestion de l'information de touch. Android envoie d'abord l'information à la vue (sur laquelle on appui bien sûr) de plus haut niveau, c'est à dire celle étant en sommet de pile. Si cette dernière ne consomme pas l'évenement, il est retransmis à la vue directement inférieure. Cette action se répète jusqu'à ce qu'une vue consomme l'évènement. Lorsqu'aucune vue ne consomme l'évènement, c'est l'activité qui sert de dernier “recourt”.
On réitère maintenant l'opération mais en activant la seconde vue (c'est à dire Z = 2). Une pression suivie d'un mouvement et d'un relâchement sur la vue Z = 1 (vue rouge) donne la sortie suivante :
05-05 16:52:53.104: DEBUG/TouchActivity(723): mZ1: ACTION_DOWN
05-05 16:52:53.114: DEBUG/TouchActivity(723): mZ2: ACTION_DOWN
05-05 16:52:55.585: DEBUG/TouchActivity(723): mZ2: ACTION_MOVE
05-05 16:52:56.826: DEBUG/TouchActivity(723): mZ2: ACTION_MOVE
05-05 16:52:57.214: DEBUG/TouchActivity(723): mZ2: ACTION_UP
En gardant la même configuration et répétant la même procédure mais sur la vue Z = 3, on revient au premier cas cité : l'activité est donc la dernière à gérer l'évènement.
Pour résumer, Android passe simplement l'information de la vue de plus haut niveau à la vue de plus bas niveau. Ce passage d'information est effectué jusqu'à ce qu'une vue consomme (renvoie true
lors de la gestion du TouchEvent
). Dès lors que l'évènement ACTION_DOWN a été consommé, l'ensemble des évènements suivants (ACTION_DOWN et ACTION_UP) sont directement envoyés à la vue dite cible.
Interception de TouchEvent
s
La gestion des TouchEvent
s est, après étude succincte, tout à fait logique. Il existe également une autre méthode permettant de contourner certains problèmes relatifs aux ViewGroup
s. En effet, prenons un ViewGroup
contenant un ensemble d'ImageView
. L'utilisateur peut faire “glisser” les images de l'une à l'autre un peu à l'instar de la galerie photo de l'iPhone. Il peut être intéressant de faire en sorte que la ViewGroup
gère directement sans se soucier du retour des onTouchEvent(MotionEvent event)
des ImageView
s. Pour ce faire, on utilise la méthode onInterceptTouchEvent(MotionEvent event)
.
La documentation incluse dans le SDK explique de façon exhaustive cette méthode et peut se résumer de la façon suivante :
L'évènement ACTION_DOWN est reçu ici (comprendre la
ViewGroup
)L'évènement circule de vue en vue comme expliqué précédemment. Si la méthode
onTouchEvent(MotionEvent event)
reçoit l'évènement ACTION_DOWN, il suffit de faire en sorte que cette dernière renvoittrue
. Ainsi les évènements suivants ne sont plus reçus par la méthodeonInterceptTouchEvent(MotionEvent event)
mais passe directement àonTouchEvent(MotionEvent event)
Tant que cette méthode retourne
false
, les évènements seront envoyés à la fois dans cette méthode et à la vue cible (celle ayant consommé l'ACTION_DOWN)Si
true
est retourné, aucun autre évènement n'est envoyé à cette méthode et la vue cible reçoit l'action ACTION_DOWN. Les évènements suivant sont envoyés à la méthodeonTouchEvent(MotionEvent event)
de laViewGroup
Les méthodes disponibles et les façons de récupérer les TouchEvent
s sous Android sont nombreuses. Notez, tout de même, que la philosophie d'Android est de gérer, le plus possible, les évènements au niveau le plus bas dans l'arbre (sur les feuilles) et donc au niveau View
. Evitez au maximum de gérer par exemple, les TouchEvent
s au niveau de l'Activity
.