I have 2 RecyclerViews - Overview & Detail RV. When a user clicks on an item in Overview RV, the clicked View is added as a Shared Element in the Fragment Transaction and transitions to Detail RV.
The motion part of the transition works very well. However, after the transition finishes, the shared element View is not removed from Detail RV! When I scroll Detail RV, I can see 2 instances of the shared element! One scrolling with the rest of the content (which is what I want), and the other stuck at a fixed position on the screen. Only when I scroll the shared element out & back into the RV's viewport, forcing a rebind, will the unwanted shared element disappear.
What is causing the shared element View not being removed automatically?
This is how I add the clicked View as a target for shared element transitions.
// OverviewAdapter.kt:
view.setOnClickListener { view ->
val sharedElements = getAllChildrenWithTransitionNames(view) // returns Map<String,View>
supportFragmentManager.beginTransaction()
.replace(R.id.fragmentContainer, DetailFragment())
.addToBackStack(null)
.also{ ft ->
ft.setReorderingAllowed(true)
sharedElements.forEach { (tn, view) ->
ft.addSharedElement(view, tn)
}
}
.commit()
}
This is how I prepare the shared element transition in DetailFragment. The clicked item will always be the first unique item in DetailFragment, so it is guaranteed to be drawn.
// DetailFragment.kt
class DetailsTransition : TransitionSet() {
init {
addTransition(ChangeBounds())
addTransition(ChangeTransform())
addTransition(ChangeImageTransform())
}
}
val listAnimator = MyRVItemAnimator()
override fun onCreate(saved: Bundle?){
listAnimator.blockAnimations = true // Detail RV has an item animator. This blocks animations during transitions to prevent flicker
sharedElementEnterTransition = DetailsTransition().also {
it.addListener(
onEnd = {
listAnimator.blockAnimations = false
}
)
}
// DetailAdapter.kt
override fun onBindViewHolder(holder: VH, position: Int) {
detailFragment.startPostponedEnterTransition()
}
I've also made sure all transitionNames
are unique and matching. So why isn't the shared element View being removed automatically after the transition finishes?
Turns out my RV ItemAnimator had a bug in it. When I was blocking animations, I did not call dispatchAdd/RemoveFinished(holder)
. This caused the shared element View to remain behind.