android - Jetpack Navigation 中从 RecyclerView 到 Detail Fragment 的共享元素转换

标签 android kotlin android-animation android-transitions android-jetpack

我正在尝试使用 Fragment 之间共享元素的简单动画进行转换。在第一个 fragment 中,我在 RecyclerView 中有元素,在第二个 fragment 中 - 完全相同的元素(在单独的 xml 布局中定义,列表中的元素也是这种类型)在顶部,在 View 的其余部分中有详细信息。我正在为 bindViewHolder 中的所有元素和目标 fragment 的 onCreateView 中的所有元素提供各种 transitionNames 我正在阅读它们并将它们设置为我想要进行过渡的元素。无论如何动画没有发生,我没有任何其他想法。下面是我从源和目标 fragment 和列表适配器中获取的代码 fragment :

列表适配器:

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    val item = list[position]
    ViewCompat.setTransitionName(holder.view, item.id)
    holder.view.setOnClickListener {
        listener?.onItemSelected(item, holder.view)
    }
    ...
}

interface interactionListener {
    fun onItemSelected(item: ItemData, view: View)
}

ListFragment(来源):

override fun onItemSelected(item: ItemData, view: View) {
    val action = ListFragmentDirections.itemDetailAction(item.id)
    val extras = FragmentNavigatorExtras(view to view.transitionName)
    val data = Bundle()
    data.putString("itemId", item.id)
    findNavController().navigate(action.actionId, data, null, extras)
}

SourceFragmentLayout:

<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    android:id="@+id/pullToRefresh"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:listitem="@layout/item_overview_row" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

DetailFragment(目标):

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    val rootView = inflater.inflate(R.layout.fragment_detail, container, false)

    val itemId = ItemDetailFragmentArgs.fromBundle(arguments).itemId

    (rootView.findViewById(R.id.includeDetails) as View).transitionName = itemId

    sharedElementEnterTransition = ChangeBounds().apply {
        duration = 750
    }
    sharedElementReturnTransition= ChangeBounds().apply {
        duration = 750
    }
    return rootView
}

细节 fragment 布局:

<include
android:id="@+id/includeDetails"
layout="@layout/item_overview_row"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

ItemOverviewRowLayout(这个作为项目包含在 recyclerView 中,并作为 header 包含在目标 fragment 中):

<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:orientation="vertical" >

我还制作了另一个应用程序,它使用 Jetpack 导航、共享元素和由同一 layout.xml 描述的元素,并且它正在运行,因为我没有从 recyclerView 过渡到目标 fragment 。也许我在这里错了,将 transitionName 设置为在目标 fragment 中找到 View ?我不知道如何以另一种方式实现它,因为目标包含布局的 ID 应该是唯一的,因为 recyclerView 项目。

最佳答案

好的,我发现使用共享元素进入动画应该是什么样子: 在 DetailFragment(目标)中,您应该在启动 onViewCreated 时运行 postponeEnterTransition()(我的 onCreateView 代码可以移动到 onViewCreated)。现在您有时间使用 transitionName 签署目标 View 元素。加载数据和查看结束后,您必须运行 startPostponedEnterTransition()。如果你不这样做,ui 会卡住,所以你不能在 postponeEnterTransition 和 startPostponedEnterTransition 之间进行耗时操作。

无论如何,现在的问题是返回转换。因为当然是同样的情况——你必须在释放动画之前重新加载 recyclerView。当然你也可以使用postponeEnterTransition(即使是return transition)。就我而言,我有 LiveData 包装的列表。在源 fragment 中,生命周期观察者正在检查数据。还有另一个挑战——如何确定数据是否加载。理论上,对于 recyclerView,您可以使用有用的内联函数:

inline fun <T : View> T.afterMeasure(crossinline f: T.() -> Unit) {
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
    override fun onGlobalLayout() {
        if (measuredWidth > 0 && measuredHeight > 0) {
            viewTreeObserver.removeOnGlobalLayoutListener(this)
            f()
        }
    }
})

...在您应用布局管理器和适配器的代码中,您可以像这样使用它:

recyclerView.afterMeasure { startPostponedEnterTransition() }

它应该确定返回动画应该开始的时间(您必须确定 recyclerView 项目中的 transitionNames 是否正确,以便过渡可以有目标 View 项目)

关于android - Jetpack Navigation 中从 RecyclerView 到 Detail Fragment 的共享元素转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52874175/

相关文章:

android - Compose LazyColumn 键,对项目进行排序时滚动会变得困惑

kotlin - Kotlin Coroutine Flow api是否具有Rx Subject等桥接工具?

android - 回收站 View : different ItemAnimators for different viewTypes

android - 动画师类型的预期资源 [ResourceType]

java - 未找到包含字段 id 的所需 View ,但 id 存在

java - 抽屉导航无法点击页面(Android Studio)

Kotlin:无法使用 "return body"函数样式一次应用两个条件

android - 消失前在复选框中显示勾号

java - 保存图像到解析

android - fragment popbackstack 动画不起作用