I have a simple app showing a gallery of images. Upon clicking one of them I want it to transition into a detail fragment. Clicking the detail fragment will pop the fragment backstack. The transition works but it always leaves an offset on the right on enter animation and an offset at the top on exit animation.
Initial
Enter
Exit
Transition set
<?xml version="1.0" encoding="utf-8"?>
<transitionSet
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="100"
>
<changeBounds/>
<changeClipBounds/>
<changeTransform/>
<changeImageTransform/>
</transitionSet>
RecyclerView item XML
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"
/>
<ImageView
android:id="@+id/image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
/>
</FrameLayout>
Detail XML
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
/>
</FrameLayout>
Open detail fragment logic
override fun openImageFragment(imageView: ImageView) {
val fragment = ImageFragment.newInstance(imageView.transitionName)
fragment.drawable = imageView.drawable
supportFragmentManager
.beginTransaction()
.setReorderingAllowed(true)
.addToBackStack(null)
.addSharedElement(imageView, imageView.transitionName)
.replace(R.id.container_fragment, fragment)
.commit()
}
Detail fragment
class ImageFragment : Fragment() {
private lateinit var binding: FragmentImageBinding
var drawable: Drawable? = null
companion object {
const val KEY_IMAGE = "IMAGE"
fun newInstance(transitionName: String): ImageFragment {
val bundle = Bundle().also {
it.putString(KEY_IMAGE, transitionName)
}
val fragment = ImageFragment()
fragment.arguments = bundle
return fragment
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
postponeEnterTransition()
val transition = TransitionInflater.from(requireContext()).inflateTransition(R.transition.scale_image)
transition.addListener(object : TransitionListenerAdapter() {
override fun onTransitionEnd(transition: Transition) {
binding.root.setOnClickListener {
requireActivity().supportFragmentManager.popBackStack()
}
}
})
sharedElementEnterTransition = transition
sharedElementReturnTransition = transition
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentImageBinding.inflate(inflater, container, false)
binding.imageView.transitionName = arguments?.getString(KEY_IMAGE)
binding.imageView.setImageDrawable(drawable)
binding.imageView.doOnPreDraw { startPostponedEnterTransition() }
return binding.root
}
}
This seems to be a bug with fragment to fragment transition. I "solved" it by switching to an activity.