Détecter les gestes courants

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.

Un geste tactile se produit lorsqu'un utilisateur place un ou plusieurs doigts sur l'écran tactile et que votre application interprète ce schéma de touches comme un geste. La détection des gestes comporte deux phases :

  1. Collecte des données d'événements tactiles.
  2. Interpréter les données pour déterminer si elles répondent aux critères des gestes pris en charge par 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 plus anciens. 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() se déclenche sur la vue qui reçoit les événements tactiles. Pour chaque séquence d'événements tactiles (position, pression, taille et ajout d'un autre doigt, par exemple) 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 lorsque le système suit la position du ou des doigts de l'utilisateur et se termine en capturant l'événement final du dernier doigt de l'utilisateur quittant l'écran. Tout au long de cette interaction, le 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 l'intéresse se produit.

Capturer les é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. Cela vous donne les données brutes dont vous avez besoin pour déterminer si un geste qui vous intéresse 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 tels que les suivants dans Logcat lorsque l'utilisateur appuie, touche et maintient le doigt, et 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-tap, le toucher prolongé, le balayage, etc., vous pouvez tirer parti de la classe GestureDetector. GestureDetector vous permet de détecter plus facilement les gestes courants sans avoir à traiter vous-même les événements tactiles individuels. Pour en savoir plus, consultez Détecter les gestes.

Capturer les événements tactiles pour une seule vue

Au lieu d'utiliser onTouchEvent(), vous pouvez associer un objet View.OnTouchListener à n'importe quel objet View à l'aide de la méthode setOnTouchListener(). Cela permet d'écouter les é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;
    }
});

Faites attention à ne pas créer d'écouteur qui renvoie false pour l'événement ACTION_DOWN. Si vous le faites, 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 de 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. Voici quelques-uns des gestes qu'il prend en charge : onDown(), onLongPress() et onFling(). Vous pouvez utiliser GestureDetector en association avec la méthode onTouchEvent() décrite précédemment.

Détecter tous les gestes acceptés

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 spécifique se produit. Pour permettre à votre objet GestureDetector de recevoir des événements, remplacez la méthode onTouchEvent() de la vue ou de l'activité, et transmettez tous les événements observés à l'instance du détecteur.

Dans l'extrait de code suivant, une valeur de retour de true à partir des méthodes on<TouchEvent> individuelles indique que l'événement tactile est géré. Une valeur de retour de false transmet les événements à travers la pile de vues jusqu'à ce que l'événement tactile soit géré avec succès.

Si vous exécutez l'extrait de code suivant dans une application de test, vous pouvez vous faire 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 verrez 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 souhaitez traiter uniquement 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 remplacer uniquement 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 que 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