De Dribbble à Android Motion



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


  1. 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 ;
  2. 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);
  3. Créez une copie de l'élément sélectionné dans la liste;
  4. Nous rendons l'élément sélectionné invisible (pas une copie, mais l'élément lui-même);
  5. Ajoutez la copie créée à l'étape 3 à la disposition racine du fragment parent, dans notre cas, c'est RecyclerFragment ;
  6. 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;
  7. Une fois l'animation terminée, créez une transaction et affichez DetailsFragment ;
  8. 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"/> // In RecyclerFragment details_toolbar_helper.translationY = -details_toolbar_helper.height 

image

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!

Source: https://habr.com/ru/post/fr423235/


All Articles