2个RecyclerViews之间的Android共享元素转换
Posted
技术标签:
【中文标题】2个RecyclerViews之间的Android共享元素转换【英文标题】:Android shared element transition between 2 RecyclerViews 【发布时间】:2019-01-20 16:31:41 【问题描述】:我在 2 个活动(MainActivity
和 DetailActivity
)中的 2 个 RecyclerView
项目之间使用默认共享元素转换。从MainActivity
到DetailActivity
的动画效果很好,但是如果用户滚动到DetailActivity
中的新项目,则重新输入动画会将项目移到顶部。我根据需要修改了android Developers Blog 上共享的示例。这是我的代码的Github Link。我还尝试在 DetailActivity
上禁用退出动画,并尝试将退出动画更改为仅淡入淡出,但这几乎就像根本不尊重退出动画一样。
这是一个视频演示(可以在最后几秒钟看到问题):
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_
android:layout_
android:animateLayoutChanges="false"
>
<View android:id="@+id/otherContent"
android:layout_
android:layout_
android:background="@android:color/holo_green_light"
/>
<android.support.v7.widget.RecyclerView
android:id="@+id/itemGallery"
android:layout_
android:layout_
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_
android:layout_
android:background="@android:color/black"
android:animateLayoutChanges="false"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/detailGallery"
android:layout_
android:layout_
android:orientation="horizontal"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
</FrameLayout>
【问题讨论】:
我不知道我是否错过了它,但我找不到当您从 DetailActivity 返回 MainActivity 时将 SharedElement 更改为新项目的代码。我希望这是有道理的 刚刚被审查删除了我的答案 - 尽管它可能是正确的答案 - 只是因为我在那里要求提供更多细节,我不会再浪费时间了...... @ArchieG.Quiñones:抱歉,我无法关注。您介意发布答案吗?伪代码也会有所帮助。在 onBackPressed() 我无权访问共享元素。 @MartinZeitler 不确定哪些审核政策阻止了您。如果您可以在评论中询问,我可以添加更多详细信息。您是否可以将您的想法概括为要点并在 cmets 中分享链接?如果可行,我可以呼吁 SO 模组接受您的回答。 如果你把这个项目的代码分享给我,我会更好地帮助你。我认为这里的问题是,从详细活动返回到 MainActivity 后,您必须将共享元素转换重新设置为正确的项目。 【参考方案1】:请检查GithubLink上的共享元素转换代码
SharedElementTransition-master.zip 是来自您的源代码的更新代码,Transition 在两个 RecyclerView 之间工作。
android-gallery-master.zip 是另一个在 RecyclerView 和 ViewPager 之间进行转换的代码。
希望它对你有用。我会尽快添加解释。
【讨论】:
谢谢!重新进入动画现在完美。从 MainActivity 到 DetailActivity 的过渡看起来不像从 Detail 到 Main 的返回那么顺利。正常吗?如果您能帮助解决这个问题并添加一些关于您所做更改的解释,那就太好了,因为我没有看到提交历史记录。我现在将答案标记为已接受,但希望您能尽快更新答案! @Jaguar 我会尽快检查 MainActivity 到 DetailActivity 的过渡部分,并会尽快提供解释。过渡在第二个演示中运行得非常顺利,即 RecyclerView 和 ViewPager 之间的过渡。看看它,请保持您的 Internet 上的 bz 图像是从其中的 URL 加载的。您也可以使用该代码作为替代解决方案。很高兴为您提供帮助。 不幸的是,我不能使用 ViewPager,因为它是一个照片应用程序,我可能有 100 张图片 - 否则 Google 使用 ViewPager 的示例(我在我的问题中发布)是一个很好的例子。跨度> 好吧。然后我将再次检查 Enter Transition 代码,使其与 exit 一样顺畅。 @Jaguar 请在 MainActivity.kt 中添加window.exitTransition = null
并在 setContentView 下面的 DetailActivity.kt 中添加 window.enterTransition = null;
并检查 Enter Transition 是否正常工作或不。如果您仍然遇到问题,请告诉我。以上是关于2个RecyclerViews之间的Android共享元素转换的主要内容,如果未能解决你的问题,请参考以下文章
两个recyclerviews的Android ItemTouchHelper
Android:以编程方式在片段中添加多个 RecyclerView
在滚动之前,RecyclerView项目有时无法正确显示(仅限Genymotion平板电脑)
Android Stackoverflow错误:ViewGroup.jumpDrawablesToCurrentState