Touch Screen (Doigts en contact sur l'écran)

 

Introduction

Il s'agit du cas simple parmis les différentes manières qu'a l'utilisateur pour envoyer des données, puisqu'il s'agit pour l'utilisateur de toucher l'écran. Trois évènements principaux peuvent alors se produire:

- Lorsque vous posez votre doigt sur l'écran

- Lorsque votre doigt quitte l'écran

- Lorsque votre doigt glisse sur l'écran

Nous récupérerons évidemment la position relative sur l'interface.

En complément, nous pourrons traiter le multitouche, c'est à dire plusieurs doigts qui touchent l'écran. Mais là, il y a des trucs à dire sur ce point...

Image non trouvée !Pour tester le touch screen sur l'émulateur, il faut ajouter au niveau hadware de votre appareil virtuel le "touch-screen support". Pour cela, lancez avd manager (accessible sous Eclipse depuis le menu "window"). Cliquez sur l'appareil virtuel qui sera utilisé et demandez à l'éditer. Cliquez ensuite sur le bouton new au niveau hardware et ajouter "touch-screen support". Sinon, l'émulateur ne réagiera pas aux cliques de la souris. Sachez enfin que l'émulateur n'est pas capable de gérer le multitouche.

Image non trouvée !Sur un androphone réel, les informations récupérées sur les positions à l'écran seront plus ou moins fiables, cela dépendra surtout de la qualité de votre appareil !

 

Le mono-contact

Tout d'abord, pour pouvoir traiter le touch screen, il nous faut une instance dérivant de la classe view (TextView, RenderView, ...).

Les coordonnées que nous récupèrerons seront données par rapport à la position de cette instance sur l'écran (Les coordonnées pourront donc être négatives ou positives par rapport à cette origine) et par rapport à l'écran lui-même (positions absolues sur l'écran)

 

Ensuite, pour pouvoir recevoir des évènements sur la position de notre doigt, nous allons implémenter un listener qui sera attaché à notre instance dérivant de la classe view. Pour cela nous implémenterons OnTouchListener.

Le code à implémenter sera :

public boolean onTouch(View v, MotionEvent event)

Avec en entrée

v qui est l'instance de view utilisée.

event l'instance d'évènement que nous recevrons.

En sortie, un booléen à vrai pour indiquer que notre listener a bien traité l'évènement, à false sinon, laissant ainsi le système le traiter.

 

MotionEvent en mono-contact

Il nous reste maintenant à traiter l'évènement:

Nous pouvons récupérer le type d'action à partir de la méthode public final int getAction ().

Celle-ci nous retournera une des différentes valeurs qui sont:

MotionEvent.ACTION_DOWN - Vous posez votre doigt à l'écran

MotionEvent.ACTION_MOVE - Vous déplacez votre doigt sur l'écran

MotionEvent.ACTION_CANCEL - ???

MotionEvent.ACTION_UP - Vous relevez votre doigt de l'écran.

Image non trouvée !A partir de l'API level 5, il sera préférable d'effectuer une opération sur la valeur retournée de l'action:

int action = event.getAction()& MotionEvent.ACTION_MASK. Car les bits de 8 à 15 peuvent altérer le résultat de la méthode ! (cf. multi-touches)

 

Nous pourrons évidemment récupérer la position sur l'écran via deux méthodes:

- Par rapport à une instance de view : public final float getX () et public final float getY ()

- Par rapport à une position absolue sur l'écran: public final float getRawX() et public final float getRawY().

Ces méthodes retournent un float, mais il s'agit bien de coordonnées en pixel que nous traiterons.

 

Il existe encore deux méthodes: getPressure() qui retourne une valeur entre 0 et 1 (pression sur l'écran) et getSize (0 pour contact confirmé ou 1 peut-être eu un contact mais tellement légé que cela peut signifier que l'utilisateur n'a pas forcement voulu ce contact !)

 

Exemple mono-contact

Dans cet exemple, nous allons utiliser un textview pour tester le touch screen. Le textview servira dans le même temps à afficher le type d'action et les coordonnées récupérées.

(On peut envisager l'utilisation d'une classe anonyme pour le listener - Des exemples avec d'autres listener: Sur le bouton ou radio-group)

 

Historique des déplacements

Le système va concerver et nous restituer un historique des mouvements effectués (évidemment limité en taille).

Cet historique contiendra:

- les positions x et y précédentes: event.getHistoricalX(i), event.getHistoricalY(i).

- L'heure des évènements précédents: event.getHistoricalEventTime(i)

- Pression...rien à voir avec la bière: event.getHistoricalPressure(i)

- contact confirmé ou non:event.getHistoricalSize(i)

avec i un index sur tous les évènements du plus récent (indice 0) au plus vieux (dont la valeur sera récupérée via la méthode event.getHistorySize())

et event, l'instance d'évènement reçu dans notre méthode.

Voici un exemple graphique cette fois, car plus facile à représenter:

Ce programme affiche un point blanc pour le dernier point préssé et un point variant du jaune pour le plus récent au rouge pour le plus vieux.

 

 

 

Multi-contacts

Le multi-contacts facile:

Depuis Android en version 2 (à partir de l'API level 5), il est possible de poser plusieurs doigts sur l'écran.

Pour commencer facilement cette partie, sachez que le principe est exactement le même que pour le mono-touch:

- Un listener que nous implémenterons: OnTouchListener

- MotionEvent pour récupérer les informations.

 

Un histoire d'index:

Mais maintenant, tout cela va se compliquer !

Les méthodes comme getX(), getY(), ... ne sont plus les seules méthodes utilisables, car nous avons plusieurs doigts, or ces deux méthodes ne retournent qu'une seule valeur. Elles sont donc complétées par des méthodes équivalentes mais indexées.

Donc nous aurons un getX(0), getX(1), ... getX(n). Cet index (nommé pointer index en anglais) est une valeur qui va varier entre 0 et n où n correspond au nombre de doigts -1 qui touchent l'écran. Il s'agit en quelque sorte d'un index interne à une sorte de tableau de valeurs que le système gère comme il l'entend.

Le problème, c'est que l'on ne peut pas associer cet index à un doigt précis touchant l'écran. En effet, il suffit que le 2ième doigt soit retiré de l'écran pour que les index des contacts suivants se voient recalculés (pas de troue permis dans l'indexation). Par exemple si votre pouce est le 3ième doigt à toucher l'écran, son pointer index sera à 2 (3-1 car tableau commençant à 0). Si vous retirez le premier doigt posé, le système va réordonner les index, votre pouce pourra se retrouver avec un index valant 1, le 0 pour l'autre doigt toujours en contact sur l'écran.

Par conséquent, l'index pour un même doigt peut changer dans le temps !!!

Il existe donc un autre index (ne pas confondre avec le doigt du même nom) qui se nomme pointer identifier (ou pointer id) qui lui reste identique dans le temps pour un doigt donné. Heureusement, il existe une méthode qui nous retournera la valeur du pointer identifier par rapport au pointer index (relisez lentement cette dernière phrase si vous n'avez pas compris...):

MotionEvent.getPointerId(int pointerIndex)

 

Nombre de contacts:

Pour connaître le nombre de doigts actuellement sur l'écran, nous passerons par la méthode MotionEvent.getPointerCount().

 

Action:

Pour récupérer l'action maintenant, nous utiliserons toujours la méthode getAction. Mais nous allons devoir faire un et logique en utilisant un masque qui se nommera MotionEvent.ACTION_MASK. L'explication arrive tout de suite après. Ce qui donnera :

int action = event.getAction()& MotionEvent.ACTION_MASK;

Les valeurs seront toujours les mêmes:

MotionEvent.ACTION_DOWN - Vous posez votre premier doigt à l'écran

MotionEvent.ACTION_MOVE - Vous déplacez un doigt sur l'écran

MotionEvent.ACTION_UP - Vous relevez votre dernier doigt de l'écran.

 

Image non trouvée !Le dernier doigt levé n'était pas forcement le premier posé ! Vous ne pouvez donc pas utiliser ces valeurs pour reconnaître systématiquement le tout premier doigt !

 

Deux nouvelles actions viennent cependant compléter la liste:

MotionEvent.ACTION_POINTER_DOWN - Vous posez un doigt supplémentaire à l'écran

MotionEvent.ACTION_POINTER_UP - Vous enlevez un doigt à l'écran, mais il en reste encore au moins un posé

 

Le mystère du MotionEvent.ACTION_MASK enfin résolu

Il sera possible de déterminer la valeur du pointer index correspondant à un doigt qui touche ou ne touche plus l'écran.

Cette information sera disponible pour toutes les actions sauf MotionEvent.ACTION_MOVE qui n'a pas de raison d'informer un ajout ou suppression, puisque rien ne bouge...du moins dans le nombre de contates !

Mais là, c'est un beau bordel ! Comment connaître la valeur de nouvel index ou de celui qui est supprimé ?

Vous allez me dire: "Tout simple, il y a une méthode qui nous retourne la valeur héhé !"

Vous pensez pas si bien dire ! Mais là aussi ce n'est pas simple, car il s'agit d'utiliser la méthode getAction() ! Cette méthode va nous retourner à la fois l'action, mais aussi le pointer index du doigt qui touche ou ne touche plus l'écran. Comment ?

Tout "simplement" en utilisant les bits 8 à 15 de la valeur qui est retournée (Vou ahurissez là non ?!).

Il faudra donc effacer les bits supérieurs à 15 (et dans le même temps ceux concernant l'action en cours soit les bits 0 à 7, bien que là, l'opération suivante fera la même chose)

Puis faire une rotation binaire à droite pour récupérer l'index dans une plage de valeur allant de 0 à 255...ce qui se fait de la manière suivante:

int pointerIndex = (event.getAction()& MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;

Et voilà, à vouloir faire compliqué, ils se sont plantés sur les noms des constantes ! Nous voulons le pointer index et ils utilisent le terme pointer id dans le nom des constantes !!!

A partir de l'API LEVEL 8, ces constantes sont marquées DEPRECATED et sont renommées ce qui donne:

int pointerIndex = (event.getAction()& MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;

(Les anciennes constantes restent donc utilisables, mais il faudrait utiliser les nouveaux noms maintenant. Reste à voir la compatibilité souhaitée de votre programme !)

 

Autres

Il existe enfin une méthode qui permet de trouver la valeur du pointer index à partir du pointer identifier

int pointerIndex = event.findPointerIndex(PointerId);

Evidemment, l'historique existe toujours, mais tout comme le getX(), il faudra l'indéxer.

 

Exemple multi-contacts

Image non trouvée !Pour cet exemple, j'utilise une fonte: arial.ttf. N'oubliez pas de la mettre dans votre répertoire assets de votre projet, sinon l'application va se planter à l'exécution !

Généralement, pour mes exemples, je fais en sorte que le code soit le plus simple possible et limité à ce dont je parle.

Bon hélas, pour y voir quelque chose, le code en exemple est un peu plus compliqué.

Son but: Afficher l'action en cours sur l'écran et présenter des cubes à l'écran pour chaque contacts détectés.

Dans le Log, un peu plus de détail pour voir comment évolu le programme.

Donc tout cela utilise le graphisme 2D.

Image non trouvée !Sur un XPeria ARC S, le nombre de contacts est limité à 4 doigts. En même temps, au delà, je ne sais pas si vous pouvez voir encore quelque chose sur votre écran !