Suivre les mouvements tactiles et du curseur

Essayer Compose
Jetpack Compose est le kit d'outils d'UI recommandé pour Android. Découvrez comment utiliser la saisie tactile et les entrées dans Compose.

Cette leçon explique comment suivre les mouvements dans les événements tactiles.

Un nouvel événement onTouchEvent() est déclenché avec un événement ACTION_MOVE chaque fois que la position, la pression ou la taille du contact tactile actuel changent. Comme décrit dans Détecter les gestes courants, tous ces événements sont enregistrés dans le paramètre MotionEvent de onTouchEvent().

Comme l'interaction tactile avec les doigts n'est pas toujours la plus précise, la détection des événements tactiles est souvent basée davantage sur le mouvement que sur un simple contact. Pour aider les applications à faire la distinction entre les gestes basés sur le mouvement (comme un balayage) et les gestes sans mouvement (comme un simple appui), Android inclut la notion de touch slop. Le "touch slop" fait référence à la distance en pixels que le doigt d'un utilisateur peut parcourir avant que le geste ne soit interprété comme un geste basé sur le mouvement. Pour en savoir plus à ce sujet, consultez Gérer les événements tactiles dans un ViewGroup.

Il existe plusieurs façons de suivre les mouvements dans un geste, en fonction des besoins de votre application. Voici des exemples :

  • Position de départ et de fin d'un pointeur, par exemple lors du déplacement d'un objet à l'écran du point A au point B.
  • Direction dans laquelle se déplace le pointeur, déterminée par les coordonnées X et Y.
  • Historique. Vous pouvez trouver la taille de l'historique d'un geste en appelant la méthode MotionEvent getHistorySize(). Vous pouvez ensuite obtenir les positions, les tailles, l'heure et les pressions de chacun des événements historiques à l'aide des méthodes getHistorical<Value> de l'événement de mouvement. L'historique est utile pour afficher une trace du doigt de l'utilisateur, par exemple pour le dessin tactile. Pour en savoir plus, consultez la documentation de référence sur MotionEvent.
  • Vitesse du pointeur lorsqu'il se déplace sur l'écran tactile.

Consultez les ressources associées suivantes :

Vitesse de suivi

Vous pouvez définir un geste basé sur le mouvement, en fonction de la distance ou de la direction parcourue par le pointeur. Toutefois, la vélocité est souvent un facteur déterminant pour suivre les caractéristiques d'un geste ou déterminer si le geste a eu lieu. Pour faciliter le calcul de la vitesse, Android fournit la classe VelocityTracker. VelocityTracker vous aide à suivre la vitesse des événements tactiles. Cela est utile pour les gestes dont la vitesse fait partie des critères, comme un geste vif.

Voici un exemple qui illustre l'objectif des méthodes de l'API VelocityTracker :

Kotlin

private const val DEBUG_TAG = "Velocity"

class MainActivity : Activity() {
    private var mVelocityTracker: VelocityTracker? = null

    override fun onTouchEvent(event: MotionEvent): Boolean {

        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                // Reset the velocity tracker back to its initial state.
                mVelocityTracker?.clear()
                // If necessary, retrieve a new VelocityTracker object to watch
                // the velocity of a motion.
                mVelocityTracker = mVelocityTracker ?: VelocityTracker.obtain()
                // Add a user's movement to the tracker.
                mVelocityTracker?.addMovement(event)
            }
            MotionEvent.ACTION_MOVE -> {
                mVelocityTracker?.apply {
                    val pointerId: Int = event.getPointerId(event.actionIndex)
                    addMovement(event)
                    // When you want to determine the velocity, call
                    // computeCurrentVelocity(). Then, call getXVelocity() and
                    // getYVelocity() to retrieve the velocity for each pointer
                    // ID.
                    computeCurrentVelocity(1000)
                    // Log velocity of pixels per second. It's best practice to
                    // use VelocityTrackerCompat where possible.
                    Log.d("", "X velocity: ${getXVelocity(pointerId)}")
                    Log.d("", "Y velocity: ${getYVelocity(pointerId)}")
                }
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                // Return a VelocityTracker object back to be re-used by others.
                mVelocityTracker?.recycle()
                mVelocityTracker = null
            }
        }
        return true
    }
}

Java

public class MainActivity extends Activity {
    private static final String DEBUG_TAG = "Velocity";
        ...
    private VelocityTracker mVelocityTracker = null;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int index = event.getActionIndex();
        int action = event.getActionMasked();
        int pointerId = event.getPointerId(index);

        switch(action) {
            case MotionEvent.ACTION_DOWN:
                if(mVelocityTracker == null) {
                    // Retrieve a new VelocityTracker object to watch the
                    // velocity of a motion.
                    mVelocityTracker = VelocityTracker.obtain();
                }
                else {
                    // Reset the velocity tracker back to its initial state.
                    mVelocityTracker.clear();
                }
                // Add a user's movement to the tracker.
                mVelocityTracker.addMovement(event);
                break;
            case MotionEvent.ACTION_MOVE:
                mVelocityTracker.addMovement(event);
                // When you want to determine the velocity, call
                // computeCurrentVelocity(). Then call getXVelocity() and
                // getYVelocity() to retrieve the velocity for each pointer ID.
                mVelocityTracker.computeCurrentVelocity(1000);
                // Log velocity of pixels per second. It's best practice to use
                // VelocityTrackerCompat where possible.
                Log.d("", "X velocity: " + mVelocityTracker.getXVelocity(pointerId));
                Log.d("", "Y velocity: " + mVelocityTracker.getYVelocity(pointerId));
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                // Return a VelocityTracker object back to be re-used by others.
                mVelocityTracker.recycle();
                break;
        }
        return true;
    }
}

Utiliser la capture du pointeur

Certaines applications, comme les jeux, les clients de virtualisation et de bureau à distance, bénéficient d'un contrôle sur le pointeur de la souris. La capture du pointeur est une fonctionnalité disponible sur Android 8.0 (niveau d'API 26) et versions ultérieures. Elle permet de contrôler le pointeur en transmettant tous les événements de la souris à une vue ciblée de votre application.

Demander la capture du pointeur

Une vue de votre application ne peut demander la capture du pointeur que lorsque la hiérarchie des vues qui la contient est sélectionnée. Pour cette raison, demandez la capture du pointeur lorsqu'une action utilisateur spécifique est effectuée sur la vue, par exemple lors d'un événement onClick() ou dans le gestionnaire d'événements onWindowFocusChanged() de votre activité.

Pour demander la capture du pointeur, appelez la méthode requestPointerCapture() sur la vue. L'exemple de code suivant montre comment demander la capture du pointeur lorsque l'utilisateur clique sur une vue :

Kotlin

fun onClick(view: View) {
    view.requestPointerCapture()
}

Java

@Override
public void onClick(View view) {
    view.requestPointerCapture();
}

Une fois la requête de capture du pointeur réussie, Android appelle onPointerCaptureChange(true). Le système envoie les événements de souris à la vue ciblée de votre application tant qu'elle se trouve dans la même hiérarchie de vues que celle qui a demandé la capture. Les autres applications cessent de recevoir les événements de souris jusqu'à ce que la capture soit libérée, y compris les événements ACTION_OUTSIDE. Android fournit des événements de pointeur à partir de sources autres que la souris comme d'habitude, mais le pointeur de la souris n'est plus visible.

Gérer les événements de pointeur capturés

Une fois qu'une vue a réussi à acquérir la capture du pointeur, Android fournit les événements de souris. Votre vue ciblée peut gérer les événements en effectuant l'une des tâches suivantes :

L'exemple de code suivant montre comment implémenter onCapturedPointerEvent(MotionEvent) :

Kotlin

override fun onCapturedPointerEvent(motionEvent: MotionEvent): Boolean {
    // Get the coordinates required by your app.
    val verticalOffset: Float = motionEvent.y
    // Use the coordinates to update your view and return true if the event is
    // successfully processed.
    return true
}

Java

@Override
public boolean onCapturedPointerEvent(MotionEvent motionEvent) {
  // Get the coordinates required by your app.
  float verticalOffset = motionEvent.getY();
  // Use the coordinates to update your view and return true if the event is
  // successfully processed.
  return true;
}

L'exemple de code suivant montre comment enregistrer un OnCapturedPointerListener :

Kotlin

myView.setOnCapturedPointerListener { view, motionEvent ->
    // Get the coordinates required by your app.
    val horizontalOffset: Float = motionEvent.x
    // Use the coordinates to update your view and return true if the event is
    // successfully processed.
    true
}

Java

myView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() {
  @Override
  public boolean onCapturedPointer (View view, MotionEvent motionEvent) {
    // Get the coordinates required by your app.
    float horizontalOffset = motionEvent.getX();
    // Use the coordinates to update your view and return true if the event is
    // successfully processed.
    return true;
  }
});

Que vous utilisiez une vue personnalisée ou que vous enregistriez un écouteur, votre vue reçoit un MotionEvent avec des coordonnées de pointeur qui spécifient des mouvements relatifs tels que les deltas X ou Y, semblables aux coordonnées fournies par un trackball. Vous pouvez récupérer les coordonnées à l'aide de getX() et getY().

Libérer la capture du pointeur

La vue de votre application peut libérer la capture du pointeur en appelant releasePointerCapture(), comme indiqué dans l'exemple de code suivant :

Kotlin

override fun onClick(view: View) {
    view.releasePointerCapture()
}

Java

@Override
public void onClick(View view) {
    view.releasePointerCapture();
}

Le système peut retirer la capture de la vue sans que vous appeliez explicitement releasePointerCapture(), généralement parce que la hiérarchie de vues contenant la vue qui demande la capture perd le focus.