两个RecyclerView之间的Android共享元素转换

10
我正在使用默认的共享元素转换在2个活动(MainActivity和DetailActivity)的2个RecyclerView项目之间。从MainActivity到DetailActivity的动画效果很好,但是如果用户在DetailActivity中滚动到新项目,则重新进入动画会将该项目移动到顶部。我修改了Android Developers Blog上共享的示例以满足我的需求。这里是我的代码的Github Link链接。我还尝试禁用DetailActivity上的退出动画,并尝试将退出动画仅更改为淡出,但几乎就像不尊重退出动画一样。下面是演示视频(问题可以在最后几秒钟中看到):

enter image description here

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>

详细界面:

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>

1
我不知道是否错过了,但我找不到代码,在从 DetailActivity 返回 MainActivity 时,更改 SharedElement 为新项。我希望这说得清楚。 - Archie G. Quiñones
刚刚我的回答被审核删除了,尽管它可能是正确的答案,只因为我在那里要求了一些更多的细节,而我不想再浪费时间在这上面了... - Martin Zeitler
@ArchieG.Quiñones:抱歉,我无法理解。您能否发布一个答案?伪代码也会有所帮助。在onBackPressed()中,我无法访问共享元素。 - dev
@MartinZeitler 不确定是哪些审查政策阻止了您。如果您可以在评论中要求更多细节,我可以添加任何更多的细节。您是否可以将您的想法放在 gist 中并在评论中分享链接?如果可以的话,我可以向 SO 模块提出申诉以接受您的答案。 - dev
你能把你的项目发布到 GitHub 上吗? - azizbekian
显示剩余5条评论
1个回答

1
请查看GithubLink上的共享元素转换代码。 SharedElementTransition-master.zip 是你的源代码更新后的代码,其中转换在两个RecyclerView之间工作。 android-gallery-master.zip 是另一份代码,其中转换在RecyclerView和ViewPager之间工作。
希望对您有所帮助。我会尽快添加说明。

谢谢!重新进入动画现在完美运作。从MainActivity到DetailActivity的过渡看起来不像从Detail返回Main那样流畅,这正常吗?如果您能帮忙修复并添加一些解释,因为我没有看到提交历史记录,那就太好了。我现在标记答案为已接受,但希望您很快更新您的答案! - dev
@Jaguar 我会尽快检查MainActivity到DetailActivity的转换部分,并很快提供解释。在第二个演示中,即RecyclerView和ViewPager之间的转换非常顺畅。请查看它,并保持互联网连接,因为其中的图像正在从URL加载。您也可以使用该代码作为替代解决方案。很高兴能帮助您。 - Viraj Patel
很遗憾,我不能使用ViewPager,因为这是一个照片应用程序,我可能有数百张图片 - 否则谷歌提供的使用ViewPager的示例(我在我的问题中发布了)是一个很好的跟随示例。 - dev
好的,那我会再检查一遍输入转换代码,以使退出更加顺畅。 - Viraj Patel
@Jaguar 请在 MainActivity.kt 中添加 window.exitTransition = null,在 setContentView 下方的 DetailActivity.kt 中添加 window.enterTransition = null;,并检查进入转换是否正常工作。如果您仍然遇到问题,请告诉我。 - Viraj Patel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接