RFM
RFM

Reputation: 153

Android multiple shared elements not working with fragments

I want to transition between fragments with two shared elements. But the problem is: only one gets animated.

The shared elements are taken from a viewHolder from list in the first fragment. The transitions I use are from Material library, exactly these ones https://medium.com/androiddevelopers/material-motion-with-mdc-c1f09bb90bf9

Fragment A:

fragmentManager.commit {
            replace([...])
            setReorderingAllowed(true)
            addSharedElement(viewA, viewA.transitionName)
            addSharedElement(viewB, viewB.transitionName)
            addToBackStack(fragmentClass.qualifiedName)
        }

Fragment B:

 sharedElementReturnTransition = MaterialContainerTransform().apply {
            scrimColor = Color.TRANSPARENT
            duration = 3000L
            interpolator = DecelerateInterpolator()
        }
 binding.headerContainer.header.cover.transitionName = arguments?.getString("firstTrName") ?: return
 binding.headerContainer.title.cover.transitionName = arguments?.getString("secondTrName") ?: return
 binding.headerContainer.header.setImageResource(arguments?.getString("image") ?: return)
 binding.headerContainer.title.text = arguments?.getString("title") ?: return

It seems like only the last element that I added in the commit{} is the one that transitions. I have checked everything, transition names are unique, all data is there in the second fragment, all transition names, it's not an argument problem. Any ideas? Help would be highly appreciated, this is a very important client request.

Upvotes: 1

Views: 1472

Answers (1)

hakans
hakans

Reputation: 31

https://material.io/develop/android/theming/motion

Unlike traditional Android shared elements, it is not designed around a singular piece of shared content, such as an image, to be moved between two scenes. Instead, the shared element here refers to the bounding container of a start View or ViewGroup (e.g. the entire row layout of an item in a list) transforming its size and shape into that of an end View or ViewGroup (e.g. the root ViewGroup of a full screen Fragment). These start and end container Views are the “shared element” of a container transform. While these containers are being transformed, their contents are swapped to create the transition.

Two shared element doesn't work properly. When selecting start view from the top of the list, imageView becomes a container. When selecting from the bottom, textView becomes a container. (See difference between example1 and example5 from the gif below)

You can use traditional shared element transition instead of Material Motion.


EXAMPLE AND GIF

fragmentEnd.sharedElementEnterTransition = MaterialContainerTransform().apply {
    scrimColor = Color.TRANSPARENT
    containerColor = Color.TRANSPARENT
    duration = 1000L
    interpolator = DecelerateInterpolator()
}

fragmentEnd.sharedElementReturnTransition = MaterialContainerTransform().apply {
    scrimColor = Color.TRANSPARENT
    containerColor = Color.TRANSPARENT
    duration = 1000L
    isElevationShadowEnabled = false
    interpolator = DecelerateInterpolator()
}

val manager: FragmentManager = activity.supportFragmentManager
manager.beginTransaction()
        .setReorderingAllowed(true)
        .addSharedElement(imageView, imageView.transitionName)
        .addSharedElement(textView, textView.transitionName)
        .addToBackStack(null)
        .replace([...])
        .commit()

enter image description here


SOLUTION

You can change transition like this:

fragmentEnd.sharedElementEnterTransition = getTransition() 
fragmentEnd.sharedElementReturnTransition = getTransition() 

private fun getTransition(): Transition{
    val set = TransitionSet()
    set.ordering = TransitionSet.ORDERING_TOGETHER
    set.addTransition(ChangeBounds())
    set.addTransition(ChangeImageTransform())
    set.addTransition(ChangeTransform())
    return set
}

Proper way of using MaterialContainerTransform:

  • Share one element

      fragmentEnd.sharedElementEnterTransition = MaterialContainerTransform().apply {
          scrimColor = Color.TRANSPARENT
          containerColor = ResourcesCompat.getColor(activity.resources,R.color.purple_amethyst,null)
          duration = 1000L
          startShapeAppearanceModel = ShapeAppearanceModel().withCornerSize(100f)
          interpolator = DecelerateInterpolator()
      }
    
    
     fragmentEnd.sharedElementReturnTransition = MaterialContainerTransform().apply {
          scrimColor = Color.TRANSPARENT
          containerColor = ResourcesCompat.getColor(activity.resources,R.color.purple_amethyst,null)
          duration = 1000L
          endShapeAppearanceModel = ShapeAppearanceModel().withCornerSize(100f)
          interpolator = DecelerateInterpolator()
      }
    
    
      val manager: FragmentManager = activity.supportFragmentManager
      manager.beginTransaction()
              .setReorderingAllowed(true)
              .addSharedElement(imageView, imageView.transitionName)
              .addToBackStack(null)
              .replace([...])
              .commit()
    
  • FragmentEnd Layout:

      <androidx.constraintlayout.widget.ConstraintLayout
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:id="@+id/container"
          android:background="@color/purple_amethyst">
    
         <....../>
    
      </androidx.constraintlayout.widget.ConstraintLayout>
    
  • Set transition name to top layout of fragmentEnd

    ViewCompat.setTransitionName(binding.container, getArguments().getString("imageViewTransitionName"));
    

RESULT

enter image description here

Upvotes: 1

Related Questions