Le dessin d'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 agissent comme des objets réels. Par exemple, ne laissez pas les images de votre application disparaître 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 perçoivent même les comportements ou les sensations subtiles d'une interface et réagissent mieux aux subtilités qui imitent le monde réel. Par exemple, lorsque les utilisateurs font glisser un objet d'interface utilisateur, donnez-leur une sensation d'inertie au début qui retarde le mouvement. À la fin du mouvement, donnez-leur une sensation d'élan qui fait passer l'objet au-delà du glissement.
Cette page explique comment utiliser les fonctionnalités du framework Android pour ajouter ces comportements réels à votre vue personnalisée.
Pour en savoir plus, consultez Présentation des événements d'entrée et Présentation de l'animation de propriétés.
Gérer les gestes de saisie
Comme de nombreux autres frameworks d'interface utilisateur, Android est compatible avec un modèle d'événement d'entrée. Les actions de l'utilisateur se transforment en événements qui déclenchent des rappels, et vous pouvez remplacer les rappels pour personnaliser la façon dont votre application répond à 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).
Remplacez 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 ne sont pas particulièrement utiles en eux-mêmes. Les interfaces utilisateur tactiles modernes définissent les interactions en termes de gestes tels que le tapotement, le tirage, la poussée, le glissement et le zoom. Pour convertir les événements tactiles bruts en gestes, Android fournit GestureDetector.
Construisez 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. Cela est nécessaire, car tous les gestes commencent par un message onDown(). Si vous renvoyez false à partir de onDown(), comme le fait GestureDetector.SimpleOnGestureListener, 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. Ne renvoyez false à partir de onDown() que si vous souhaitez ignorer un geste entier.
Une fois que vous avez 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 puissant de contrôler les appareils à écran tactile, mais ils peuvent être contre-intuitifs et difficiles à mémoriser, sauf s'ils produisent des résultats physiquement plausibles.
Supposons, par exemple, que vous souhaitiez implémenter un geste de glissement horizontal qui fait tourner l'élément dessiné dans la vue autour de son axe vertical. Ce geste est logique si l'interface utilisateur répond en se déplaçant rapidement dans le sens du glissement, puis en ralentissant, comme si l'utilisateur appuyait sur un volant d'inertie et le faisait tourner.
La documentation sur l'animation d'un geste de défilement explique en détail comment implémenter votre propre comportement de défilement. Mais simuler la sensation d'un volant d'inertie n'est pas simple. Il faut beaucoup de physique et de mathématiques pour qu'un modèle de volant d'inertie 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 de type volant d'inertie.
Pour démarrer un glissement, appelez fling() avec la vélocité de départ et les valeurs x et y minimales et maximales du glissement. Pour la valeur de vélocité, 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. Ensuite, mettez à jour le 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 la position x et y à ce moment-là. Appelez getCurrX() et getCurrY() pour récupérer ces valeurs.
La plupart des vues transmettent directement les positions x et y de l'objet Scroller à scrollTo().
Cet exemple est un peu différent : il utilise la position x de défilement 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 elle n'applique pas automatiquement ces positions à votre vue. Appliquez de nouvelles coordonnées suffisamment souvent pour que l'animation de défilement soit fluide. Il existe deux façons de procéder :
- 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 le décalage de défilement change. - Configurez un
ValueAnimatorpour animer pendant la durée du glissement 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.
Rendre vos transitions fluides
Les utilisateurs s'attendent à ce qu'une interface utilisateur moderne passe d'un état à un autre de manière fluide : les éléments d'interface utilisateur apparaissent et disparaissent progressivement au lieu d'apparaître et de disparaître, et les mouvements commencent et se terminent en douceur au lieu de démarrer et de s'arrêter brusquement. Le framework d'animation de propriétés Android facilite les transitions fluides.
Pour utiliser le système d'animation, chaque fois qu'une propriété modifie l'apparence de votre vue, ne la modifiez pas 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, l'animation est encore plus simple, car les vues disposent d'un
ViewPropertyAnimator
intégré qui est 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();