Get started

Enable setReorderingAllowed(true) . It allows to reorder a calling the lifecycle methods and your animation will be displayed correctly. Call addSharedElement() and add the views that will be shared between screens Add unique android:transitionName on each view in transition Add sharedElementEnterTransition/sharedElementReturnTransition in destination fragment. Optionally: for better effect we can also set enterTransition/exitTransition . Add postponeEnterTransition/startPostponedEnterTransition to define the moment when the data is loaded and UI is ready to be drawn

Broken animation



Mistake #1. Static transitionNames (half a day wasted)

transitionName

val transitionNameImage = context.getString(R.string.transition_image, title)

Mistake #2. Not considering parent fragment (1.5 days wasted)

postponeEnterTransition()/startPostponedEnterTransition()

Here it is override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) parentFragment?.also { parentFragment -> NewsTransitioner.setupFirstFragment(parentFragment) parentFragment.postponeEnterTransition() } // Initialization UI }



Mistake #3. Underestimating Glide (2 days wasted)

Current animation



Glide .with(target) .load(url) .apply( RequestOptions().dontTransform() // this line )

Mistake #4. Incorrectly managing postPostponeTransition()

postPostponeTransition()

startPostponedEnterTransition()

on the one hand, when images with transition are loaded and ready

on the other hand, the views hierarchy are measured and laid out

RequestListener<Drawable> { override fun onLoadFailed( e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean ): Boolean { startPostponedEnterTransition() return false } override fun onResourceReady( resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean ): Boolean { startPostponedEnterTransition() return false } }

startPostponedEnterTransition()

doOnPreDraw()

startPostponedEnterTransition()

Putting all together

Keep in mind fragments hierarchy (don’t forget about parent fragment) In case with RecyclerView, always build transition names dynamically (source + destination) Disable any Glide transformations Do calls postPostponeTransition() and startPostponedEnterTransition() correctly regarding your logic.

Final animation



Example fragment.sharedElementEnterTransition = TransitionSet().apply { addTransition(ChangeImageTransform()) addTransition(ChangeBounds()) addTransition(ChangeTransform()) addTransition(ChangeOutlineRadiusTransition(animationRadiusData.startRadius, animationRadiusData.endRadius) ) }



There is a good practice to make your application beautiful and live, and nowadays there are a lot of tools and ways to achieve this. One of them is Shared Element Transition.In this article I’ll cover a few mistakes which have cost me a lot of time; I’ll show how to avoid them if you decide to implement this kind of transitions with Fragments on application. Before making the animation I’ve read dozens of articles but most of them were about Activity Transition. However, I came across a really good ones about Fragments and I want to give a little recap on how to create Shared Element Transition.Here are main steps to create animation:Seems like that’s enough to build animation and make your designers and users happy. BUT there are always some accidents. Let’s take a look what we’ll have if we take the steps listed above:That’s not what we expected. Let’s figure it out.As I said before, our Views should have unique transition names — otherwise the transition framework won’t be able to recognize which View take part with transition and will make it without them. So where is a problem?The thing is RecyclerView. That’s it.If there is RecyclerView and an animating view is a part of RecyclerView item, you should build and setdynamically (forget about XML). Besides, you should do it in both fragments even if the second one doesn’t have RecyclerView.So the fix is:You might have noticed I put «title» as an argument to get a unique name. It’s better to use domain model instead of, for instance, item position on the screen, because there is no need to pass these names as arguments to Bundle and parse them from the second Fragment.I know, you might ask «How come?». But when I was reading the articles about shared animation, no one considered an example in complex fragment’s hierarchy. That’s why you might sometimes not pay attention to it.So, my first fragment was a child of fragment’s container and no wonder thathad no effect.What you need to do here is to call these methods from parent fragment.«Ok, I’ve learnt how to make shared transition, when to call required methods regarding the lifecycle and the loading. This time it’s gonna work!»Well I was wrong. This is perhaps the trickiest mistake I’ve faced. Let’s take a look at what we have so far:You may notice there is a weird glitch with enter transition. When it starts, the image has already changed matrix and then just move to the final position.I don’t want to describe the whole investigation here. Long story short, I was lucky to stumble across a nice article Where I found solution. Here it is:“We have this glitch because Glide tries to optimize image loading. By default, Glide is resizing and trimming images to match the target view.”In order to fix it, I added, no jokes, a single line of code like this to initialization Glide’s chain:So, you should disable any Glide’s transformations on images if they’re involved in a shared transition.Honestly, it’s not exactly a mistake but still I assume it would be good to mention.When it comes to manageandmethods, you should select the right moment. The moment is when the UI is already to be drawn.There are two main points we should know before calling the methods:For images usually we use Glide and it has a fancy listener:Note that we callin both cases: success and error. It’s vital because if we don’t make it your application will freeze.In cases when transition comes without any images you may useextension method from ktx on the root layout and doover there.: Speaking of RecyclerView, it’s not enough to simply add a listener for images. We should retain an item position of RecyclerView where transition starts from. When the user goes back to the previous screen, we should compare image loaded position with the retained position at the listener and start transition only when they are matched.In this article, I’ve showed some gotchas you might face implementing a shared transition with fragments and the ways to deal with them.Briefly, here they are:Thank you for reading and see you next time!P.S. If you wonder how to animate image corners, here the code , just add to the rest shared animation transitions.