Détecter les gestes courants

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 tactile se produit lorsqu'un utilisateur place un ou plusieurs doigts sur l'écran tactile et que votre application interprète ce motif de pression comme un geste. La détection des gestes se déroule en deux phases:

  1. Collecte de données sur les événements tactiles...
  2. Interpréter les données pour déterminer si elles répondent aux critères liés aux gestes compatibles avec votre application

Classes AndroidX

Les exemples de ce document utilisent les classes GestureDetectorCompat et MotionEventCompat. Ces classes se trouvent dans la bibliothèque AndroidX. Utilisez les classes AndroidX dans la mesure du possible pour assurer la compatibilité avec les appareils précédents. MotionEventCompat ne remplace pas la classe MotionEvent. Il fournit plutôt des méthodes utilitaires statiques auxquelles vous transmettez votre objet MotionEvent pour recevoir l'action associée à cet événement.

Recueillir des données

Lorsqu'un utilisateur place un ou plusieurs doigts sur l'écran, le rappel onTouchEvent() est déclenché sur la vue qui reçoit les événements tactiles. Pour chaque séquence d'événements tactiles (comme la position, la pression, la taille ou l'ajout d'un autre doigt) identifiée comme un geste, onTouchEvent() est déclenché plusieurs fois.

Le geste commence lorsque l'utilisateur touche l'écran pour la première fois, se poursuit à mesure que le système suit la position de son ou de ses doigts, et se termine par l'enregistrement de l'événement final du dernier doigt de l'utilisateur qui quitte l'écran. Tout au long de cette interaction, l'élément MotionEvent envoyé à onTouchEvent() fournit les détails de chaque interaction. Votre application peut utiliser les données fournies par MotionEvent pour déterminer si un geste qui lui tient à cœur se produit.

Enregistrer des événements tactiles pour une activité ou une vue

Pour intercepter les événements tactiles dans un Activity ou un View, remplacez le rappel onTouchEvent().

L'extrait de code suivant utilise getAction() pour extraire l'action effectuée par l'utilisateur à partir du paramètre event. Vous obtenez ainsi les données brutes dont vous avez besoin pour déterminer si un geste qui vous tient à cœur se produit.

Kotlin

class MainActivity : Activity() {
    ...
    // This example shows an Activity. You can use the same approach if you are 
    // subclassing a View.
    override fun onTouchEvent(event: MotionEvent): Boolean {
        return when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                Log.d(DEBUG_TAG, "Action was DOWN")
                true
            }
            MotionEvent.ACTION_MOVE -> {
                Log.d(DEBUG_TAG, "Action was MOVE")
                true
            }
            MotionEvent.ACTION_UP -> {
                Log.d(DEBUG_TAG, "Action was UP")
                true
            }
            MotionEvent.ACTION_CANCEL -> {
                Log.d(DEBUG_TAG, "Action was CANCEL")
                true
            }
            MotionEvent.ACTION_OUTSIDE -> {
                Log.d(DEBUG_TAG, "Movement occurred outside bounds of current screen element")
                true
            }
            else -> super.onTouchEvent(event)
        }
    }
}

Java

public class MainActivity extends Activity {
...
// This example shows an Activity. You can use the same approach if you are
// subclassing a View.
@Override
public boolean onTouchEvent(MotionEvent event){
    switch(event.getAction()) {
        case (MotionEvent.ACTION_DOWN) :
            Log.d(DEBUG_TAG,"Action was DOWN");
            return true;
        case (MotionEvent.ACTION_MOVE) :
            Log.d(DEBUG_TAG,"Action was MOVE");
            return true;
        case (MotionEvent.ACTION_UP) :
            Log.d(DEBUG_TAG,"Action was UP");
            return true;
        case (MotionEvent.ACTION_CANCEL) :
            Log.d(DEBUG_TAG,"Action was CANCEL");
            return true;
        case (MotionEvent.ACTION_OUTSIDE) :
            Log.d(DEBUG_TAG,"Movement occurred outside bounds of current screen element");
            return true;
        default :
            return super.onTouchEvent(event);
    }
}

Ce code génère des messages semblables aux suivants dans Logcat lorsque l'utilisateur appuie, appuie dessus et maintient la pression, puis fait glisser:

GESTURES D   Action was DOWN
GESTURES D   Action was UP
GESTURES D   Action was MOVE

Pour les gestes personnalisés, vous pouvez ensuite traiter ces événements vous-même pour déterminer s'ils représentent un geste que vous devez gérer. Toutefois, si votre application utilise des gestes courants, tels que le double appui, l'appui prolongé, l'action glisser d'un geste vif, etc., vous pouvez tirer parti de la classe GestureDetector. GestureDetector vous permet de détecter plus facilement les gestes courants sans traiter vous-même les événements tactiles individuels. Ce point est abordé plus en détail dans la section Détecter les gestes.

Capturer les événements tactiles sur une seule vue

Au lieu de onTouchEvent(), vous pouvez associer un objet View.OnTouchListener à n'importe quel objet View à l'aide de la méthode setOnTouchListener(). Cela permet d'écouter des événements tactiles sans sous-classer un View existant, comme illustré dans l'exemple suivant:

Kotlin

findViewById<View>(R.id.my_view).setOnTouchListener { v, event ->
    // Respond to touch events.
    true
}

Java

View myView = findViewById(R.id.my_view);
myView.setOnTouchListener(new OnTouchListener() {
    public boolean onTouch(View v, MotionEvent event) {
        // Respond to touch events.
        return true;
    }
});

Veillez à créer un écouteur qui renvoie false pour l'événement ACTION_DOWN. Dans ce cas, l'écouteur n'est pas appelé pour la séquence d'événements ACTION_MOVE et ACTION_UP suivante. En effet, ACTION_DOWN est le point de départ pour tous les événements tactiles.

Si vous créez une vue personnalisée, vous pouvez remplacer onTouchEvent(), comme décrit précédemment.

Détecter les gestes

Android fournit la classe GestureDetector pour détecter les gestes courants. Certains des gestes compatibles incluent onDown(), onLongPress() et onFling(). Vous pouvez utiliser GestureDetector conjointement avec la méthode onTouchEvent() décrite précédemment.

Détecter tous les gestes compatibles

Lorsque vous instanciez un objet GestureDetectorCompat, l'un des paramètres qu'il accepte est une classe qui implémente l'interface GestureDetector.OnGestureListener. GestureDetector.OnGestureListener avertit les utilisateurs lorsqu'un événement tactile particulier se produit. Pour permettre à votre objet GestureDetector de recevoir des événements, remplacez la méthode onTouchEvent() de la vue ou de l'activité, puis transmettez tous les événements observés à l'instance de détecteur.

Dans l'extrait de code suivant, une valeur renvoyée true par les méthodes on<TouchEvent> individuelles indique que l'événement tactile est géré. Une valeur renvoyée de false transmet les événements vers le bas dans la pile des vues jusqu'à ce que l'appui soit correctement traité.

Si vous exécutez l'extrait suivant dans une application de test, vous pouvez avoir une idée de la façon dont les actions sont déclenchées lorsque vous interagissez avec l'écran tactile et du contenu de MotionEvent pour chaque événement tactile. Vous voyez ensuite la quantité de données générées pour les interactions simples.

Kotlin

private const val DEBUG_TAG = "Gestures"

class MainActivity :
        Activity(),
        GestureDetector.OnGestureListener,
        GestureDetector.OnDoubleTapListener {

    private lateinit var mDetector: GestureDetectorCompat

    // Called when the activity is first created.
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Instantiate the gesture detector with the
        // application context and an implementation of
        // GestureDetector.OnGestureListener.
        mDetector = GestureDetectorCompat(this, this)
        // Set the gesture detector as the double-tap
        // listener.
        mDetector.setOnDoubleTapListener(this)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        return if (mDetector.onTouchEvent(event)) {
            true
        } else {
            super.onTouchEvent(event)
        }
    }

    override fun onDown(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDown: $event")
        return true
    }

    override fun onFling(
            event1: MotionEvent,
            event2: MotionEvent,
            velocityX: Float,
            velocityY: Float
    ): Boolean {
        Log.d(DEBUG_TAG, "onFling: $event1 $event2")
        return true
    }

    override fun onLongPress(event: MotionEvent) {
        Log.d(DEBUG_TAG, "onLongPress: $event")
    }

    override fun onScroll(
            event1: MotionEvent,
            event2: MotionEvent,
            distanceX: Float,
            distanceY: Float
    ): Boolean {
        Log.d(DEBUG_TAG, "onScroll: $event1 $event2")
        return true
    }

    override fun onShowPress(event: MotionEvent) {
        Log.d(DEBUG_TAG, "onShowPress: $event")
    }

    override fun onSingleTapUp(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onSingleTapUp: $event")
        return true
    }

    override fun onDoubleTap(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDoubleTap: $event")
        return true
    }

    override fun onDoubleTapEvent(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDoubleTapEvent: $event")
        return true
    }

    override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onSingleTapConfirmed: $event")
        return true
    }

}

Java

public class MainActivity extends Activity implements
        GestureDetector.OnGestureListener,
        GestureDetector.OnDoubleTapListener{

    private static final String DEBUG_TAG = "Gestures";
    private GestureDetectorCompat mDetector;

    // Called when the activity is first created.
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Instantiate the gesture detector with the
        // application context and an implementation of
        // GestureDetector.OnGestureListener.
        mDetector = new GestureDetectorCompat(this,this);
        // Set the gesture detector as the double-tap
        // listener.
        mDetector.setOnDoubleTapListener(this);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        if (this.mDetector.onTouchEvent(event)) {
            return true;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean onDown(MotionEvent event) {
        Log.d(DEBUG_TAG,"onDown: " + event.toString());
        return true;
    }

    @Override
    public boolean onFling(MotionEvent event1, MotionEvent event2,
            float velocityX, float velocityY) {
        Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString());
        return true;
    }

    @Override
    public void onLongPress(MotionEvent event) {
        Log.d(DEBUG_TAG, "onLongPress: " + event.toString());
    }

    @Override
    public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
            float distanceY) {
        Log.d(DEBUG_TAG, "onScroll: " + event1.toString() + event2.toString());
        return true;
    }

    @Override
    public void onShowPress(MotionEvent event) {
        Log.d(DEBUG_TAG, "onShowPress: " + event.toString());
    }

    @Override
    public boolean onSingleTapUp(MotionEvent event) {
        Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString());
        return true;
    }

    @Override
    public boolean onDoubleTap(MotionEvent event) {
        Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString());
        return true;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent event) {
        Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString());
        return true;
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent event) {
        Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString());
        return true;
    }
}

Détecter un sous-ensemble de gestes compatibles

Si vous ne souhaitez traiter que quelques gestes, vous pouvez étendre GestureDetector.SimpleOnGestureListener au lieu d'implémenter l'interface GestureDetector.OnGestureListener.

GestureDetector.SimpleOnGestureListener fournit une implémentation pour toutes les méthodes on<TouchEvent> en renvoyant false pour chacune d'elles. Cela vous permet de ne remplacer que les méthodes qui vous intéressent. Par exemple, l'extrait de code suivant crée une classe qui étend GestureDetector.SimpleOnGestureListener et remplace onFling() et onDown().

Que vous utilisiez GestureDetector.OnGestureListener ou GestureDetector.SimpleOnGestureListener, il est recommandé d'implémenter une méthode onDown() qui renvoie true. En effet, tous les gestes commencent par un message onDown(). Si vous renvoyez false à partir de onDown(), comme le fait GestureDetector.SimpleOnGestureListener par défaut, le système suppose que vous souhaitez ignorer le reste du geste, et les autres méthodes de GestureDetector.OnGestureListener ne sont pas appelées. Cela peut entraîner des problèmes inattendus dans votre application. Ne renvoyez false à partir de onDown() que si vous souhaitez vraiment ignorer un geste entier.

Kotlin

private const val DEBUG_TAG = "Gestures"

class MainActivity : Activity() {

    private lateinit var mDetector: GestureDetectorCompat

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mDetector = GestureDetectorCompat(this, MyGestureListener())
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        mDetector.onTouchEvent(event)
        return super.onTouchEvent(event)
    }

    private class MyGestureListener : GestureDetector.SimpleOnGestureListener() {

        override fun onDown(event: MotionEvent): Boolean {
            Log.d(DEBUG_TAG, "onDown: $event")
            return true
        }

        override fun onFling(
                event1: MotionEvent,
                event2: MotionEvent,
                velocityX: Float,
                velocityY: Float
        ): Boolean {
            Log.d(DEBUG_TAG, "onFling: $event1 $event2")
            return true
        }
    }
}

Java

public class MainActivity extends Activity {

    private GestureDetectorCompat mDetector;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mDetector = new GestureDetectorCompat(this, new MyGestureListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        if (this.mDetector.onTouchEvent(event)) {
              return true;
        }
        return super.onTouchEvent(event);
    }

    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
        private static final String DEBUG_TAG = "Gestures";

        @Override
        public boolean onDown(MotionEvent event) {
            Log.d(DEBUG_TAG,"onDown: " + event.toString());
            return true;
        }

        @Override
        public boolean onFling(MotionEvent event1, MotionEvent event2,
                float velocityX, float velocityY) {
            Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString());
            return true;
        }
    }
}

Ressources supplémentaires