
Sur Internet, il existe de nombreux modèles animés intéressants avec de belles interfaces utilisateur d'applications mobiles, mais il n'y a pas beaucoup d'exemples avec la mise en œuvre de ces interfaces. Malgré l'abondance de divers cadres et outils intégrés dans le SDK Android, il n'est souvent pas facile pour un développeur débutant d'implémenter une belle interface utilisateur, y compris avec un modèle prêt à l'emploi.
Dans cet article, nous allons essayer d'implémenter l'interface utilisateur développée par le designer Ivan Parfyonov pour le studio PLATES.
Créez d'abord deux fragments:
RecyclerFragment
et
DetailsFragment
.
Framework de transition Android?
Le cadre de transition Android fonctionne bien, mais il y a quelques nuances. Premièrement, nous voulons que tout fonctionne pour nous au moins sur l'API 19, et deuxièmement, nous devons animer plusieurs éléments utilisateur en même temps et certains d'entre eux sont présents dans un seul fragment. Par conséquent, nous implémentons l'animation de l'élément de transition (transition d'élément partagé) manuellement à l'aide de
ViewPropertyAnimator
.
Tout en ordre
- Nous calculons les coordonnées finales de l'élément sélectionné à partir de la liste (ses coordonnées dans
DerailsFragment
), la liste est un RecyclerView
; - Nous enregistrons les coordonnées actuelles (coordonnées dans
RecyclerFragment
) et les passons à DetailsFragment
(cela est nécessaire pour une animation inverse avec API <21); - Créez une copie de l'élément sélectionné dans la liste;
- Nous rendons l'élément sélectionné invisible (pas une copie, mais l'élément lui-même);
- Ajoutez la copie créée à l'étape 3 à la disposition racine du fragment parent, dans notre cas, c'est
RecyclerFragment
; - Nous commençons l'animation des éléments d'interface restants et déplaçons la copie créée aux coordonnées finales de l'étape 1;
- Une fois l'animation terminée, créez une transaction et affichez
DetailsFragment
; - Nous commençons l'animation des éléments d'interface dans
DetailsFragment
.
Animation des éléments de l'interface utilisateur
Pour animer la
Toolbar
nous allons créer une
View
supplémentaire dans le
RecyclerFragment
et la placer derrière l'écran en haut. Cette
View
sera animée dans le conteneur de la
Toolbar
dans
DetailsFragment
(couleur bleue sur gif) à l'aide de
ViewPropertyAnimator
.
<View android:id="@+id/details_toolbar_helper" android:layout_width="wrap_content" android:layout_height="@dimen/details_toolbar_container_height" android:background="@color/colorPrimary" app:layout_constraintTop_toTopOf="parent"/>
BottomNavigationView
animations
BottomNavigationView
et
RecyclerView
BottomNavigationView
également implémentées à l'aide de
ViewPropertyAnimator
, rien de compliqué (changement de transparence et déplacement).
Un peu du cadre de transition
En termes simples, le cadre de transition Android, lorsqu'il commence à animer l'élément de transition, crée une copie du contenu de cet élément de transition (quelque chose comme un écran d'impression), crée une ImageView à partir de cette copie, puis ajoute cette image au calque de superposition supplémentaire dans le calque de superposition le fragment appelé et démarre l'animation.
Le cadre de transition Android ne nous convient pas, car lorsque l'animation de l'élément de transition commence, tous les autres éléments de l'interface utilisateur du fragment sont détruits et nous ne pouvons pas les animer. C'est-à-dire lorsque nous cliquons sur un élément de la liste dans
RecyclerFragment
pour ouvrir
DetailsFragment
et démarrer l'animation de transition, tous les autres éléments d'interface de
RecyclerFragment
détruits sans animation.
Pour obtenir le résultat souhaité, nous allons créer manuellement une copie de l'élément sélectionné dans la liste, l'ajouter au calque de superposition puis l'animer. Mais ici, un petit problème apparaît, la documentation de la
ViewGroupOverlay add(view: View)
dit:
Si la vue a un parent, la vue sera supprimée de ce parent avant d'être ajoutée à la superposition.
Mais pour
RecyclerView
cela ne fonctionne pas, l'élément sélectionné n'est pas supprimé de
RecyclerView
après avoir été ajouté à la couche de superposition.
Voici ce qui se passe lorsque nous ajoutons l'élément sélectionné à la couche de superposition:
Et nous en avons besoin:
Par conséquent, nous n'utiliserons pas la couche de superposition et nous ajouterons la copie immédiatement à la disposition racine. Créez une copie du contenu de l'élément sélectionné, ajoutez-le à
ImageView
et définissez les coordonnées:
fun View.copyViewImage(): View { val copy = ImageView(context) val bitmap = drawToBitmap() copy.setImageBitmap(bitmap) // pre-Lollipop , card view , card view return (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { CardView(context).apply { cardElevation = resources.getDimension(R.dimen.card_elevation) radius = resources.getDimension(R.dimen.card_corner_radius) addView(copy) } } else { copy }).apply { layoutParams = this@copyViewImage.layoutParams layoutParams.height = this@copyViewImage.height layoutParams.width = this@copyViewImage.width x = this@copyViewImage.x y = this@copyViewImage.y } }
Pourquoi créer une copie si vous pouvez simplement animer l'élément directement sélectionné dans la liste?Parce que
RecyclerView
lui-même sera également animé et, par conséquent, tous ses éléments, y compris celui sélectionné, que nous voulons animer séparément.
Après cela, ajoutez une copie au balisage racine et démarrez l'animation.
override fun onClick(view: View) { val fragmentTransaction = initFragmentTransaction(view) val copy = view.createCopyView() root.addView(copy) view.visibility = View.INVISIBLE startAnimation(copy, fragmentTransaction) }
Et voici ce que nous avons obtenu:
Ligne d'arrivée
L'animation gif ci-dessus se déroule dans un
RecyclerFragment
, et après son achèvement, nous devons montrer
DetailsFragment
.
.withEndAction { fragmentTransaction?.commitAllowingStateLoss() }
Pourquoi utilisons-nous commitAllowingStateLoss
?Si vous ne l'utilisez pas et qu'au moment de l'animation, il y aura, par exemple, un changement d'orientation de l'écran, nous obtiendrons
IllegalStateExeption
.
Ici, il est bien écrit à ce sujet.
Ensuite, nous commençons l'animation des éléments d'interface utilisateur nécessaires dans
DetailsFragment
.
Courez le tout ensemble
Pas tout à fait la même chose que l'original, mais il semble similaire.
Des exemples
Le code source est disponible sur
GitHub , et l'article est également disponible en
anglais .
Merci de votre attention!