Dessiner une interface utilisateur n'est qu'une partie de la création d'une vue personnalisée. Vous devez également faire en sorte que votre vue réponde à l'entrée utilisateur d'une manière qui ressemble beaucoup à l'action réelle que vous imitez.
Faites en sorte que les objets de votre application se comportent comme des objets réels. Par exemple, ne laissez pas les images de votre application sortir de l'existence et réapparaître ailleurs, car les objets du monde réel ne le font pas. Déplacez plutôt vos images d'un endroit à un autre.
Les utilisateurs ressentent même un comportement ou des sentiments subtils dans une interface et réagissent mieux aux subtilités qui imitent le monde réel. Par exemple, lorsque les utilisateurs glissent un objet d'interface utilisateur, donnez-leur au début un sentiment d'inertie qui retarde le mouvement. À la fin du mouvement, donnez-leur un sentiment de mouvement qui transporte l'objet au-delà du glissement d'un geste vif.
Cette page explique comment utiliser les fonctionnalités du framework Android pour ajouter ces comportements réels à votre vue personnalisée.
Vous trouverez d'autres informations associées dans les sections Présentation des événements d'entrée et Présentation des animations de propriétés.
Gérer les gestes de saisie
Comme de nombreux autres frameworks d'UI, Android est compatible avec un modèle d'événement d'entrée. Les actions des utilisateurs se transforment en événements qui déclenchent des rappels. Vous pouvez ignorer les rappels pour personnaliser la réponse de votre application à l'utilisateur. L'événement d'entrée le plus courant dans le système Android est touch, qui déclenche onTouchEvent(android.view.MotionEvent)
.
Ignorez cette méthode pour gérer l'événement comme suit:
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean { return super.onTouchEvent(event) }
Java
@Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }
Les événements tactiles en eux-mêmes ne sont pas particulièrement utiles. Les interfaces utilisateur tactiles modernes définissent les interactions en termes de gestes, comme appuyer, tirer, pousser, faire glisser et zoomer. Pour convertir les événements tactiles bruts en gestes, Android fournit GestureDetector
.
Créez un GestureDetector
en transmettant une instance d'une classe qui implémente GestureDetector.OnGestureListener
.
Si vous ne souhaitez traiter que quelques gestes, vous pouvez étendre GestureDetector.SimpleOnGestureListener
au lieu d'implémenter l'interface GestureDetector.OnGestureListener
. Par exemple, ce code crée une classe qui étend GestureDetector.SimpleOnGestureListener
et remplace onDown(MotionEvent)
.
Kotlin
private val myListener = object : GestureDetector.SimpleOnGestureListener() { override fun onDown(e: MotionEvent): Boolean { return true } } private val detector: GestureDetector = GestureDetector(context, myListener)
Java
class MyListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } } detector = new GestureDetector(getContext(), new MyListener());
Que vous utilisiez ou non GestureDetector.SimpleOnGestureListener
, implémentez toujours une méthode onDown()
qui renvoie true
. Cette étape est nécessaire, car tous les gestes commencent par un message onDown()
. Si vous renvoyez false
à partir de onDown()
, comme GestureDetector.SimpleOnGestureListener
, 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. Ne renvoyez false
à partir de onDown()
que si vous souhaitez ignorer un geste entier.
Après avoir implémenté GestureDetector.OnGestureListener
et créé une instance de GestureDetector
, vous pouvez utiliser votre GestureDetector
pour interpréter les événements tactiles que vous recevez dans onTouchEvent()
.
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean { return detector.onTouchEvent(event).let { result -> if (!result) { if (event.action == MotionEvent.ACTION_UP) { stopScrolling() true } else false } else true } }
Java
@Override public boolean onTouchEvent(MotionEvent event) { boolean result = detector.onTouchEvent(event); if (!result) { if (event.getAction() == MotionEvent.ACTION_UP) { stopScrolling(); result = true; } } return result; }
Lorsque vous transmettez à onTouchEvent()
un événement tactile qu'il ne reconnaît pas comme faisant partie d'un geste, il renvoie false
. Vous pouvez ensuite exécuter votre propre code de détection de gestes personnalisé.
Créer un mouvement physiquement plausible
Les gestes sont un moyen efficace de contrôler les appareils à écran tactile, mais ils peuvent être contre-intuitifs et difficiles à mémoriser à moins qu'ils ne produisent des résultats physiquement plausibles.
Par exemple, supposons que vous souhaitiez implémenter un geste de balayage horizontal qui définit l'élément dessiné dans la vue pivotant autour de son axe vertical. Ce geste est logique si l'interface utilisateur répond en se déplaçant rapidement dans la direction du geste vif, puis en ralentissant, comme si l'utilisateur appuie sur un volant et le fait tourner.
La documentation sur l'animation d'un geste de défilement fournit une explication détaillée sur l'implémentation de votre propre comportement Scoll. Mais simuler la sensation d'un volant n'est pas chose aisée. Un grand nombre de lois de la physique et des mathématiques sont nécessaires pour qu'un modèle de volant fonctionne correctement. Heureusement, Android fournit des classes d'assistance pour simuler ce comportement et d'autres. La classe Scroller
est la base de la gestion des gestes de glissement d'un volant.
Pour lancer un glissement d'un geste vif, appelez fling()
avec la vitesse de début, ainsi que les valeurs minimale et maximale x et y du déplacement. Pour la valeur de vitesse, vous pouvez utiliser la valeur calculée par GestureDetector
.
Kotlin
fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { scroller.fling( currentX, currentY, (velocityX / SCALE).toInt(), (velocityY / SCALE).toInt(), minX, minY, maxX, maxY ) postInvalidate() return true }
Java
@Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { scroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY); postInvalidate(); return true; }
L'appel à fling()
configure le modèle physique pour le geste de glissement d'un geste vif. Ensuite, mettez à jour Scroller
en appelant Scroller.computeScrollOffset()
à intervalles réguliers. computeScrollOffset()
met à jour l'état interne de l'objet Scroller
en lisant l'heure actuelle et en utilisant le modèle physique pour calculer les positions x et y à ce moment-là. Appelez getCurrX()
et getCurrY()
pour récupérer ces valeurs.
La plupart des vues transmettent les positions x et y de l'objet Scroller
directement à scrollTo()
.
Cet exemple est légèrement différent: il utilise la position de défilement x actuelle pour définir l'angle de rotation de la vue.
Kotlin
scroller.apply { if (!isFinished) { computeScrollOffset() setItemRotation(currX) } }
Java
if (!scroller.isFinished()) { scroller.computeScrollOffset(); setItemRotation(scroller.getCurrX()); }
La classe Scroller
calcule les positions de défilement pour vous, mais ne les applique pas automatiquement à votre vue. Appliquez suffisamment de nouvelles coordonnées pour rendre l'animation de défilement fluide. Pour cela, vous pouvez procéder de deux façons:
- Forcez un redessin en appelant
postInvalidate()
après avoir appeléfling()
. Cette technique nécessite de calculer les décalages de défilement dansonDraw()
et d'appelerpostInvalidate()
chaque fois que ce décalage change. - Configurez un
ValueAnimator
à animer pendant la durée du glissement d'un geste vif et ajoutez un écouteur pour traiter les mises à jour de l'animation en appelantaddUpdateListener()
. Cette technique vous permet d'animer les propriétés d'unView
.
Facilitez la transition
Les utilisateurs s'attendent à ce qu'une interface utilisateur moderne passe facilement d'un état à un autre: des éléments d'interface utilisateur apparaissent et disparaissent au lieu d'apparaître et de disparaître, et des mouvements commencent et se terminent en douceur au lieu de démarrer et de s'arrêter brusquement. Le framework d'animation des propriétés Android facilite les transitions fluides.
Pour utiliser le système d'animation, lorsqu'une propriété modifie ce qui affecte l'apparence de votre vue, ne modifiez pas la propriété directement. Utilisez plutôt ValueAnimator
pour effectuer la modification. Dans l'exemple suivant, la modification du composant enfant sélectionné dans la vue fait pivoter l'ensemble de la vue rendue de sorte que le pointeur de sélection soit centré.
ValueAnimator
modifie la rotation sur une période de plusieurs centaines de millisecondes, au lieu de définir immédiatement la nouvelle valeur de rotation.
Kotlin
autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0).apply { setIntValues(targetAngle) duration = AUTOCENTER_ANIM_DURATION start() }
Java
autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0); autoCenterAnimator.setIntValues(targetAngle); autoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION); autoCenterAnimator.start();
Si la valeur que vous souhaitez modifier est l'une des propriétés View
de base, il est encore plus facile d'effectuer l'animation, car les vues disposent d'un ViewPropertyAnimator
intégré optimisé pour l'animation simultanée de plusieurs propriétés, comme dans l'exemple suivant:
Kotlin
animate() .rotation(targetAngle) .duration = ANIM_DURATION .start()
Java
animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();