Gérer les gestes tactiles multipoints

Essayer Compose
Jetpack Compose est le kit d'outils d'interface utilisateur recommandé pour Android. Découvrez comment utiliser l'écran tactile et la saisie dans Compose.

Un geste multipoint se produit lorsque plusieurs pointeurs (doigts) appuient sur l'écran en même temps. Ce document explique comment détecter les gestes impliquant plusieurs pointeurs.

Suivre plusieurs pointeurs

Lorsque plusieurs pointeurs appuient sur l'écran en même temps, le système génère les événements tactiles suivants:

  • ACTION_DOWN : envoyé lorsque le premier pointeur appuie sur l'écran. Le geste est alors déclenché. Les données de ce pointeur se trouvent toujours à l'index 0 dans MotionEvent.
  • ACTION_POINTER_DOWN : envoyé lorsque des pointeurs supplémentaires apparaissent à l'écran après le premier. Vous pouvez obtenir l'index du pointeur qui vient de tomber en panne à l'aide de getActionIndex().
  • ACTION_MOVE : envoyé lorsqu'un changement se produit dans un geste, impliquant un nombre illimité de pointeurs.
  • ACTION_POINTER_UP : envoyé lorsqu'un pointeur secondaire est déclenché. Vous pouvez obtenir l'index du pointeur qui vient d'être amélioré à l'aide de getActionIndex().
  • ACTION_UP : envoyé lorsque le dernier pointeur quitte l'écran.
  • ACTION_CANCEL : indique que l'ensemble du geste, y compris tous les pointeurs, est annulé.

Gestes de début et de fin

Un geste est une série d'événements commençant par un événement ACTION_DOWN et se terminant par un événement ACTION_UP ou ACTION_CANCEL. Il y a un geste actif à la fois. Les actions DOWN, MOVE, UP et CANCEL s'appliquent à l'ensemble du geste. Par exemple, un événement avec ACTION_MOVE peut indiquer un mouvement pour tous les pointeurs vers le bas à ce moment-là.

Garder une trace des pointeurs

Utilisez l'index et l'ID du pointeur pour suivre la position des pointeurs individuels dans un MotionEvent.

  • Index: une MotionEvent stocke les informations de pointeur dans un tableau. L'index d'un pointeur correspond à sa position dans ce tableau. La plupart des méthodes MotionEvent utilisent l'index de pointeur comme paramètre plutôt que l'ID du pointeur.
  • ID: chaque pointeur possède également un mappage d'ID qui reste persistant lors des événements tactiles pour permettre le suivi d'un pointeur individuel sur l'ensemble du geste.

Les pointeurs individuels apparaissent dans un événement de mouvement dans un ordre indéterminé. Ainsi, l'index d'un pointeur peut changer d'un événement à l'autre, mais son ID reste constant tant qu'il reste actif. Utilisez la méthode getPointerId() pour obtenir l'ID d'un pointeur afin de le suivre sur tous les événements de mouvement suivants dans un geste. Ensuite, pour les événements de mouvement successifs, utilisez la méthode findPointerIndex() afin d'obtenir l'index du pointeur pour un ID de pointeur donné dans cet événement de mouvement. Par exemple :

Kotlin

private var mActivePointerId: Int = 0

override fun onTouchEvent(event: MotionEvent): Boolean {
    ...
    // Get the pointer ID.
    mActivePointerId = event.getPointerId(0)

    // ... Many touch events later...

    // Use the pointer ID to find the index of the active pointer
    // and fetch its position.
    val (x: Float, y: Float) = event.findPointerIndex(mActivePointerId).let { pointerIndex ->
        // Get the pointer's current position.
        event.getX(pointerIndex) to event.getY(pointerIndex)
    }
    ...
}

Java

private int mActivePointerId;

public boolean onTouchEvent(MotionEvent event) {
    ...
    // Get the pointer ID.
    mActivePointerId = event.getPointerId(0);

    // ... Many touch events later...

    // Use the pointer ID to find the index of the active pointer
    // and fetch its position.
    int pointerIndex = event.findPointerIndex(mActivePointerId);
    // Get the pointer's current position.
    float x = event.getX(pointerIndex);
    float y = event.getY(pointerIndex);
    ...
}

Pour prendre en charge plusieurs pointeurs tactiles, vous pouvez mettre en cache tous les pointeurs actifs avec leur ID au moment de leurs événements ACTION_POINTER_DOWN et ACTION_DOWN individuels. Supprimez les pointeurs de votre cache au niveau de leurs événements ACTION_POINTER_UP et ACTION_UP. Ces ID mis en cache peuvent vous être utiles pour gérer correctement d'autres événements d'action. Par exemple, lors du traitement d'un événement ACTION_MOVE, recherchez l'index pour chaque ID de pointeur actif mis en cache, récupérez ses coordonnées à l'aide des fonctions getX() et getY(), puis comparez ces coordonnées à vos coordonnées mises en cache pour identifier les pointeurs qui ont été déplacés.

Utilisez la fonction getActionIndex() avec les événements ACTION_POINTER_UP et ACTION_POINTER_DOWN uniquement. N'utilisez pas cette fonction avec les événements ACTION_MOVE, car elle renvoie toujours 0.

Récupérer MotionEvent actions

Utilisez la méthode getActionMasked() ou la version de compatibilité MotionEventCompat.getActionMasked() pour récupérer l'action d'un MotionEvent. Contrairement à la méthode getAction() précédente, getActionMasked() est conçu pour fonctionner avec plusieurs pointeurs. Elle renvoie l'action sans les index de pointeur. Pour les actions avec un index de pointeur valide, utilisez getActionIndex() pour renvoyer l'index des pointeurs associés à l'action, comme indiqué dans l'extrait suivant:

Kotlin

val (xPos: Int, yPos: Int) = MotionEventCompat.getActionMasked(event).let { action ->
    Log.d(DEBUG_TAG, "The action is ${actionToString(action)}")
    // Get the index of the pointer associated with the action.
    MotionEventCompat.getActionIndex(event).let { index ->
        // The coordinates of the current screen contact, relative to
        // the responding View or Activity.
        MotionEventCompat.getX(event, index).toInt() to MotionEventCompat.getY(event, index).toInt()
    }
}

if (event.pointerCount > 1) {
    Log.d(DEBUG_TAG, "Multitouch event")

} else {
    // Single touch event.
    Log.d(DEBUG_TAG, "Single touch event")
}

...

// Given an action int, returns a string description.
fun actionToString(action: Int): String {
    return when (action) {
        MotionEvent.ACTION_DOWN -> "Down"
        MotionEvent.ACTION_MOVE -> "Move"
        MotionEvent.ACTION_POINTER_DOWN -> "Pointer Down"
        MotionEvent.ACTION_UP -> "Up"
        MotionEvent.ACTION_POINTER_UP -> "Pointer Up"
        MotionEvent.ACTION_OUTSIDE -> "Outside"
        MotionEvent.ACTION_CANCEL -> "Cancel"
        else -> ""
    }
}

Java

int action = MotionEventCompat.getActionMasked(event);
// Get the index of the pointer associated with the action.
int index = MotionEventCompat.getActionIndex(event);
int xPos = -1;
int yPos = -1;

Log.d(DEBUG_TAG,"The action is " + actionToString(action));

if (event.getPointerCount() > 1) {
    Log.d(DEBUG_TAG,"Multitouch event");
    // The coordinates of the current screen contact, relative to
    // the responding View or Activity.
    xPos = (int)MotionEventCompat.getX(event, index);
    yPos = (int)MotionEventCompat.getY(event, index);

} else {
    // Single touch event.
    Log.d(DEBUG_TAG,"Single touch event");
    xPos = (int)MotionEventCompat.getX(event, index);
    yPos = (int)MotionEventCompat.getY(event, index);
}
...

// Given an action int, returns a string description
public static String actionToString(int action) {
    switch (action) {

        case MotionEvent.ACTION_DOWN: return "Down";
	case MotionEvent.ACTION_MOVE: return "Move";
	case MotionEvent.ACTION_POINTER_DOWN: return "Pointer Down";
	case MotionEvent.ACTION_UP: return "Up";
	case MotionEvent.ACTION_POINTER_UP: return "Pointer Up";
	case MotionEvent.ACTION_OUTSIDE: return "Outside";
	case MotionEvent.ACTION_CANCEL: return "Cancel";
    }
    return "";
}
Figure 1 : Modèles de dessin multipoint

Ressources supplémentaires

Pour en savoir plus sur les événements d'entrée, consultez les documentations de référence suivantes: