【问题标题】:Android shared element transition between 2 RecyclerViews2个RecyclerViews之间的Android共享元素转换
【发布时间】:2019-01-20 16:31:41
【问题描述】:

我在 2 个活动(MainActivityDetailActivity)中的 2 个 RecyclerView 项目之间使用默认共享元素转换。从MainActivityDetailActivity 的动画效果很好,但是如果用户滚动到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_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>

【问题讨论】:

  • 我不知道我是否错过了它,但我找不到当您从 DetailActivity 返回 MainActivity 时将 SharedElement 更改为新项目的代码。我希望这是有道理的
  • 刚刚被审查删除了我的答案 - 尽管它可能是正确的答案 - 只是因为我在那里要求提供更多细节,我不会再浪费时间了......
  • @ArchieG.Quiñones:抱歉,我无法关注。您介意发布答案吗?伪代码也会有所帮助。在 onBackPressed() 我无权访问共享元素。
  • @MartinZeitler 不确定哪些审核政策阻止了您。如果您可以在评论中询问,我可以添加更多详细信息。您是否可以将您的想法概括为要点并在 cmets 中分享链接?如果可行,我可以呼吁 SO 模组接受您的回答。
  • 如果你把这个项目的代码分享给我,我会更好地帮助你。我认为这里的问题是,从详细活动返回到 MainActivity 后,您必须将共享元素转换重新设置为正确的项目。

标签: android android-animation android-transitions shared-element-transition


【解决方案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 是否正常工作或不。如果您仍然遇到问题,请告诉我。
猜你喜欢
  • 1970-01-01
  • 2015-05-15
  • 1970-01-01
  • 1970-01-01
  • 2015-10-26
  • 1970-01-01
  • 2017-01-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多