Suivi des doigts tactiles multiples
Cette rubrique montre comment suivre les événements tactiles à partir de plusieurs doigts
Il existe des moments où une application tactile doit suivre les doigts individuels lorsqu’ils se déplacent simultanément sur l’écran. Une application classique est un programme de peinture à doigts. Vous souhaitez que l’utilisateur puisse dessiner avec un seul doigt, mais également dessiner avec plusieurs doigts à la fois. Lorsque votre programme traite plusieurs événements tactiles, il doit distinguer les événements correspondant à chaque doigt. Android fournit un code d’ID à cet effet, mais l’obtention et la gestion de ce code peuvent être un peu difficiles.
Pour tous les événements associés à un doigt particulier, le code d’ID reste le même. Le code d’ID est attribué lorsqu’un doigt touche d’abord l’écran et devient non valide une fois le doigt levé à partir de l’écran. Ces codes d’ID sont généralement des entiers très petits et Android les réutilise pour les événements tactiles ultérieurs.
Presque toujours, un programme qui suit les doigts individuels conserve un dictionnaire pour le suivi tactile. La clé de dictionnaire est le code d’ID qui identifie un doigt particulier. La valeur du dictionnaire dépend de l’application. Dans l’exemple FingerPaint, chaque trait de doigt (du toucher à la libération) est associé à un objet qui contient toutes les informations nécessaires pour afficher la ligne dessinée avec ce doigt. Le programme définit une petite FingerPaintPolyline
classe à cet effet :
class FingerPaintPolyline
{
public FingerPaintPolyline()
{
Path = new Path();
}
public Color Color { set; get; }
public float StrokeWidth { set; get; }
public Path Path { private set; get; }
}
Chaque polyligne a une couleur, une largeur de trait et un objet graphique Path
Android pour accumuler et afficher plusieurs points de la ligne au fur et à mesure qu’il est dessiné.
Le reste du code indiqué ci-dessous est contenu dans un View
dérivé nommé FingerPaintCanvasView
. Cette classe gère un dictionnaire d’objets de type FingerPaintPolyline
pendant le temps qu’ils sont activement dessinés par un ou plusieurs doigts :
Dictionary<int, FingerPaintPolyline> inProgressPolylines = new Dictionary<int, FingerPaintPolyline>();
Ce dictionnaire permet à la vue d’obtenir rapidement les FingerPaintPolyline
informations associées à un doigt particulier.
La FingerPaintCanvasView
classe conserve également un List
objet pour les polylignes qui ont été terminées :
List<FingerPaintPolyline> completedPolylines = new List<FingerPaintPolyline>();
Les objets dans ce List
cas sont dans le même ordre qu’ils ont été dessinés.
FingerPaintCanvasView
remplace deux méthodes définies par View
: Voir OnDraw
et OnTouchEvent
.
Dans son OnDraw
remplacement, la vue dessine les polylignes terminées, puis dessine les polylignes en cours.
La substitution de la OnTouchEvent
méthode commence par obtenir une pointerIndex
valeur de la ActionIndex
propriété. Cette ActionIndex
valeur différencie plusieurs doigts, mais elle n’est pas cohérente entre plusieurs événements. Pour cette raison, vous utilisez la pointerIndex
méthode pour obtenir la valeur du GetPointerId
pointeurid
. Cet ID est cohérent entre plusieurs événements :
public override bool OnTouchEvent(MotionEvent args)
{
// Get the pointer index
int pointerIndex = args.ActionIndex;
// Get the id to identify a finger over the course of its progress
int id = args.GetPointerId(pointerIndex);
// Use ActionMasked here rather than Action to reduce the number of possibilities
switch (args.ActionMasked)
{
// ...
}
// Invalidate to update the view
Invalidate();
// Request continued touch input
return true;
}
Notez que le remplacement utilise la ActionMasked
propriété dans l’instruction switch
plutôt que la Action
propriété. Voici pourquoi :
Lorsque vous traitez de plusieurs touches tactiles, la Action
propriété a la valeur du MotionEventsAction.Down
premier doigt pour toucher l’écran, puis les valeurs des Pointer2Down
Pointer3Down
deuxième et troisième doigts touchent également l’écran. Comme les quatrième et cinquième doigts font contact, la Action
propriété a des valeurs numériques qui ne correspondent même pas aux membres de l’énumération MotionEventsAction
! Vous devrez examiner les valeurs des indicateurs de bits dans les valeurs pour interpréter ce qu’elles signifient.
De même, lorsque les doigts quittent le contact avec l’écran, la Action
propriété a des valeurs et Pointer2Up
Pointer3Up
pour les deuxième et troisième doigts, et Up
pour le premier doigt.
La ActionMasked
propriété prend moins de valeurs, car elle est destinée à être utilisée conjointement avec la ActionIndex
propriété pour faire la distinction entre plusieurs doigts. Lorsque les doigts touchent l’écran, la propriété ne peut être égale MotionEventActions.Down
que pour le premier doigt et PointerDown
pour les doigts suivants. À mesure que les doigts quittent l’écran, ActionMasked
il y a des valeurs pour Pointer1Up
les doigts suivants et Up
pour le premier doigt.
Lorsque vous utilisez ActionMasked
, les ActionIndex
distinctions entre les doigts suivants pour toucher et quitter l’écran, mais vous n’avez généralement pas besoin d’utiliser cette valeur, sauf en tant qu’argument pour d’autres méthodes dans l’objet MotionEvent
. Pour les interactions tactiles multiples, l’une de ces méthodes les plus importantes est GetPointerId
appelée dans le code ci-dessus. Cette méthode retourne une valeur que vous pouvez utiliser pour une clé de dictionnaire pour associer des événements particuliers aux doigts.
Le OnTouchEvent
remplacement dans l’exemple traite les MotionEventActions.Down
événements de PointerDown
manière identique en créant un objet FingerPaintPolyline
et en l’ajoutant au dictionnaire :
public override bool OnTouchEvent(MotionEvent args)
{
// Get the pointer index
int pointerIndex = args.ActionIndex;
// Get the id to identify a finger over the course of its progress
int id = args.GetPointerId(pointerIndex);
// Use ActionMasked here rather than Action to reduce the number of possibilities
switch (args.ActionMasked)
{
case MotionEventActions.Down:
case MotionEventActions.PointerDown:
// Create a Polyline, set the initial point, and store it
FingerPaintPolyline polyline = new FingerPaintPolyline
{
Color = StrokeColor,
StrokeWidth = StrokeWidth
};
polyline.Path.MoveTo(args.GetX(pointerIndex),
args.GetY(pointerIndex));
inProgressPolylines.Add(id, polyline);
break;
// ...
}
// ...
}
Notez que l’utilisateur pointerIndex
est également utilisé pour obtenir la position du doigt dans la vue. Toutes les informations tactiles sont associées à la pointerIndex
valeur. L’identificateur id
unique identifie les doigts entre plusieurs messages, de sorte qu’il est utilisé pour créer l’entrée de dictionnaire.
De même, le OnTouchEvent
remplacement gère également le MotionEventActions.Up
polyline terminé et Pointer1Up
de la même façon en transférant le polyline terminé vers la completedPolylines
collection afin qu’ils puissent être dessinés pendant le OnDraw
remplacement. Le code supprime également l’entrée id
du dictionnaire :
public override bool OnTouchEvent(MotionEvent args)
{
// ...
switch (args.ActionMasked)
{
// ...
case MotionEventActions.Up:
case MotionEventActions.Pointer1Up:
inProgressPolylines[id].Path.LineTo(args.GetX(pointerIndex),
args.GetY(pointerIndex));
// Transfer the in-progress polyline to a completed polyline
completedPolylines.Add(inProgressPolylines[id]);
inProgressPolylines.Remove(id);
break;
case MotionEventActions.Cancel:
inProgressPolylines.Remove(id);
break;
}
// ...
}
Maintenant, pour la partie délicate.
Entre les événements bas et haut, il existe généralement de nombreux MotionEventActions.Move
événements. Ceux-ci sont regroupés dans un seul appel à OnTouchEvent
, et ils doivent être gérés différemment des événements et Up
des Down
événements. La pointerIndex
valeur obtenue précédemment à partir de la ActionIndex
propriété doit être ignorée. Au lieu de cela, la méthode doit obtenir plusieurs pointerIndex
valeurs en boucle entre 0 et la PointerCount
propriété, puis obtenir une id
pour chacune de ces pointerIndex
valeurs :
public override bool OnTouchEvent(MotionEvent args)
{
// ...
switch (args.ActionMasked)
{
// ...
case MotionEventActions.Move:
// Multiple Move events are bundled, so handle them differently
for (pointerIndex = 0; pointerIndex < args.PointerCount; pointerIndex++)
{
id = args.GetPointerId(pointerIndex);
inProgressPolylines[id].Path.LineTo(args.GetX(pointerIndex),
args.GetY(pointerIndex));
}
break;
// ...
}
// ...
}
Ce type de traitement permet à l’exemple de suivre les doigts individuels et de dessiner les résultats à l’écran :
Vous avez maintenant vu comment vous pouvez suivre des doigts individuels sur l’écran et faire la distinction entre eux.