GridLayout shared element transition not working correctly

Viewed 672

I'm trying to create a transition between a RecyclerView with a GridLayoutManager and a fragment. The grid shows user pictures thumbnails and when i tap the thumbnail it shows the details of the user. However, when i tap the thumbnail and show the details, the transition always starts from the top left of the screen instead of the thumbnail position.

How can i make the transition start from the thumbnail position?

This is the result i want:

Expected result

And this is what i got:

Transition result

And this is my code:

// UsersAdapter.kt
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        ViewCompat.setTransitionName(holder.itemView.picture, "picture_$position")
}

// MasterFragment.kt
// This is called when i click an item in the grid
itemClickSubject.subscribe { position ->
        val user = users[position]
        val transitionName = "picture_$position"

        fragmentManager
                ?.beginTransaction()
                ?.addSharedElement(picture, transitionName)
                ?.replace(R.id.content, DetailsFragment.create(user, transitionName))
                ?.addToBackStack(null)
                ?.commit()
    }.addTo(disposables)

// DetailsFragment.kt
class DetailsFragment : Fragment() {

    companion object {

        fun create(user: User, transitionName: String): DetailsFragment {
            val args = Bundle()
            args.putParcelable("user", user)
            args.putString("transition_name", transitionName)

            val fragment = DetailsFragment()
            fragment.arguments = args

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {                   
                fragment.sharedElementEnterTransition = ChangeBounds()
                fragment.sharedElementReturnTransition = ChangeBounds()
            }

            return fragment
        }

    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_details, container, false).apply {
            val transitionName = arguments?.getString("transition_name", "")
            ViewCompat.setTransitionName(findViewById(R.id.picture), transitionName)
        }
    }

}
1 Answers

From the code you have shared, assuming you have shared all the code added that relates to shared-element transitions, I believe you would be missing the postpone and resume shared element transitions calls.

The images we would like to transition are loaded into the grid and the pager and take time to load. To make it work properly, we will need to postpone the transition until the participating views are ready (e.g. laid out and loaded with the image data).

To do so, we call a postponeEnterTransition() in our fragments' onCreateView(), and once the image is loaded, we start the transition by calling startPostponedEnterTransition().

Note: postpone is called for both the grid and the pager fragments to support both forward and backward transitions when navigating the app.

https://android-developers.googleblog.com/2018/02/continuous-shared-element-transitions.html

Extra resource for deeper explanation:

https://www.androiddesignpatterns.com/2015/03/activity-postponed-shared-element-transitions-part3b.html

Related