I am using default shared element transitions between 2 RecyclerView items in 2 activities (MainActivity and DetailActivity). Animation from MainActivity to DetailActivity is working fine, but if user has scrolled to new item in the DetailActivity, then reenter animation shifts the item to top. I modified the sample shared on Android Developers Blog for my needs. Here's the Github Link to my code. I have also tried disabling exit animations on the DetailActivity, and tried changing exit animations to fade only, but it's almost like exit animations are not being respected at all.
Here's a video demo (issue can be seen in the last couple of seconds):

MainActivity:
class MainActivity : AppCompatActivity(), ListImageAdapter.ListImageClickListener {
private lateinit var imageData: ImageData
private lateinit var listImageAdapter: ListImageAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupGallery()
prepareTransitions()
}
@SuppressLint("RestrictedApi")
override fun onListImageClick(position: Int, imageView: ImageView) {
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("IMAGE_DATA", imageData)
val activityOptions = ActivityOptions.makeSceneTransitionAnimation(this, imageView,
ViewCompat.getTransitionName(imageView))
startActivityForResult(intent, 101, activityOptions.toBundle())
}
override fun onActivityReenter(resultCode: Int, data: Intent?) {
data?.let { intent ->
if (intent.hasExtra("IMAGE_DATA")) {
imageData = intent.getParcelableExtra("IMAGE_DATA")
listImageAdapter.images = imageData.images
val position = imageData.images.indexOfFirst { it.selected }
itemGallery.scrollToPosition(position)
}
}
super.onActivityReenter(resultCode, data)
}
private fun setupGallery() {
imageData = ImageData(getGalleryItems())
val snapHelper = PagerSnapHelper()
snapHelper.attachToRecyclerView(itemGallery)
listImageAdapter = ListImageAdapter(imageData.images, this)
itemGallery.adapter = listImageAdapter
itemGallery.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
val selectedView = snapHelper.findSnapView(itemGallery.layoutManager)
selectedView?.let {
val selectedPosition = itemGallery.layoutManager?.getPosition(selectedView)
selectedPosition?.let { onMediumGalleryItemHighlighted(selectedPosition) }
}
}
}
})
}
private fun onMediumGalleryItemHighlighted(position: Int) {
imageData.images = imageData.images.mapIndexed { index, galleryItem ->
when {
index == position -> galleryItem.copy(selected = true)
galleryItem.selected -> galleryItem.copy(selected = false)
else -> galleryItem
}
}
}
private fun prepareTransitions() {
setExitSharedElementCallback(
object : SharedElementCallback() {
override fun onMapSharedElements(names: List<String>?, sharedElements: MutableMap<String, View>?) {
val selectedPosition = imageData.images.indexOfFirst { it.selected }
val selectedViewHolder = itemGallery
.findViewHolderForAdapterPosition(selectedPosition)
if (selectedViewHolder?.itemView == null) {
return
}
sharedElements!![names!![0]] = selectedViewHolder.itemView.findViewById(R.id.listItemImage)
}
})
}
private fun getGalleryItems(): List<Image> {
return listOf(
Image(R.drawable.cat, true),
Image(R.drawable.lion, false),
Image(R.drawable.tortoise, false)
)
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="false"
>
<View android:id="@+id/otherContent"
android:layout_width="match_parent"
android:layout_height="256dp"
android:background="@android:color/holo_green_light"
/>
<android.support.v7.widget.RecyclerView
android:id="@+id/itemGallery"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_below="@+id/otherContent"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
</RelativeLayout>
DetailActivity:
class DetailActivity : AppCompatActivity() {
private lateinit var detailImageAdapter: DetailImageAdapter
private lateinit var imageData: ImageData
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detail)
imageData = intent.extras.getParcelable("IMAGE_DATA")
initViews()
prepareTransitions()
resetScrolledPosition()
}
private fun initViews() {
val snapHelper = PagerSnapHelper()
snapHelper.attachToRecyclerView(detailGallery)
detailImageAdapter = DetailImageAdapter(imageData.images)
detailGallery.adapter = detailImageAdapter
detailGallery.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
val selectedView = snapHelper.findSnapView(detailGallery.layoutManager)
selectedView?.let {
val selectedPosition = detailGallery.layoutManager?.getPosition(selectedView)
selectedPosition?.let { onItemSelected(selectedPosition) }
}
}
}
})
}
private fun resetScrolledPosition() {
val position = imageData.images.indexOfFirst { it.selected }
imageData.images = imageData.images.mapIndexed { index, galleryItem ->
when {
index == position -> {
galleryItem.copy(selected = true)
}
galleryItem.selected -> galleryItem.copy(selected = false)
else -> galleryItem
}
}
detailImageAdapter.images = imageData.images
detailGallery.scrollToPosition(position)
supportStartPostponedEnterTransition()
}
private fun onItemSelected(position: Int) {
imageData.images = imageData.images.mapIndexed { index, galleryItem ->
when {
index == position -> galleryItem.copy(selected = true)
galleryItem.selected -> galleryItem.copy(selected = false)
else -> galleryItem
}
}
}
override fun onBackPressed() {
var resultIntent = Intent()
resultIntent = resultIntent.putExtra("IMAGE_DATA", imageData)
setResult(Activity.RESULT_OK, resultIntent)
super.onBackPressed()
}
private fun prepareTransitions() {
setEnterSharedElementCallback(
object : SharedElementCallback() {
override fun onMapSharedElements(names: List<String>?, sharedElements: MutableMap<String, View>?) {
val selectedPosition = imageData.images.indexOfFirst { it.selected }
val selectedViewHolder = detailGallery.findViewHolderForAdapterPosition(selectedPosition)
if (selectedViewHolder?.itemView == null) {
return
}
sharedElements!![names!![0]] = selectedViewHolder.itemView.findViewById(R.id.detailItemImage)
}
})
}
}
activity_detail.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:animateLayoutChanges="false"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/detailGallery"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
</FrameLayout>
Please check shared element transition code on GithubLink
SharedElementTransition-master.zip is updated code from your source code in which Transition is working between two RecyclerViews.
android-gallery-master.zip is another code in which Transition is working between RecyclerView and ViewPager.
Hope it will work for you. I will add explanation soon.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With