
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 unRecyclerView;
- 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!