当前位置: 首页 > 知识库问答 >
问题:

Jetpack导航中从RecyclerView到Detail Fragment的共享元素转换

卜季萌
2023-03-14

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

列表适配器:

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)
}

源片段布局:

<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
}

详细碎片布局:

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

ItemOverviewRowLayout(此项作为项目包含在recyclerView中,并作为标题包含在目标片段中):

<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描述的元素制作了另一个应用程序,它正在工作,因为我没有从回收器视图过渡到目标片段。也许我在这里错了,将过渡名称设置为目标片段中的查找视图?我不知道如何以另一种方式制作它,因为目标包含布局的ID应该是唯一的,因为回收器视图项目。

共有2个答案

於乐语
2023-03-14

从答案来看,使用ViewTreeObserver相当耗费资源。还有很多过程要做。因此,我建议您使用doOnPreDraw,而不是在测量recyclerView后等待。代码实现如下所示。

recyclerView.doOnPreDraw {
    startPostponedEnterTransition()
}
左丘兴生
2023-03-14

好的,我发现使用共享元素输入动画应该是什么样子:在DetailFraank(Target)中,您应该在start onViewCreated上运行postPoneSetTranection()(我从onCreateView中的代码可以移动到onViewCreated)。现在您有时间使用transtionName签署目标视图元素。在加载数据和视图结束后,您必须运行start PostPonedEnterprise Trantion()。如果不这样做,用户界面会冻结,因此您无法在postPoneSetTranature和start PostPonedEnterprise Tranection之间进行耗时的操作。

无论如何,现在的问题是返回转换。当然,这是同样的情况——在发布动画之前,必须重新加载recyclerView。当然,您也可以使用延迟输入转换(即使是返回转换)。在我的例子中,我用LiveData包装了列表。在源片段中,生命周期观察者正在检查数据。还有一个挑战-如何确定是否加载了数据。理论上,通过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() }

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

 类似资料:
  • 我正在实现一个gallery应用程序,它有一个片段,其中包含一个带有图像的RecyclerView,单击一个图像,我会转到ViewPager循环浏览图像 目前,我正试图实现像本视频中那样的入门动画。问题是动画不起作用,我显然遗漏了一些东西(只是显示与转换相关的代码): 查看页面: GridAdapter: 在MainActivity中,我在onClick中实例化ViewPagerFragment:

  • 每个应用都会有自己的导航,为了让每个应用都能很方便共享自己的导航数据,我们需要一种良好的应用间导航共享机制,而这种机制就是程序内部可以访问的内部 api. 导航配置文件 导航配置文件就是用于配置应用有哪些导航api,此文件会返回一个数组,数组的子项就一个应用内部 api 链接的地址,但不用带应用名; 导航配置文件是位于应用根目录下的nav.php文件,以 portal 应用为例,就是 app/po

  • 每个应用都会有自己的导航,为了让每个应用都能很方便共享自己的导航数据,我们需要一种良好的应用间导航共享机制,而这种机制就是程序内部可以访问的内部 api. 导航配置文件 导航配置文件就是用于配置应用有哪些导航api,此文件会返回一个数组,数组的子项就一个应用内部 api 链接的地址,但不用带应用名; 导航配置文件是位于应用根目录下的nav.php文件,以 portal 应用为例,就是 app/po

  • 我一直在尝试实现这个共享元素转换,并在单击转换时不断出现“java.lang.IllegalArgumentException:共享元素不能为null”错误。请帮忙。 这是下面给出的MainActive onCreate方法。请检查我的代码。 这是recyclerView的onClickListener。 图像的过渡名称相同。这里的bug修复在使用共享元素的活动转换中出现问题并不是问题所在。 我卡

  • 如何从Jetpack撰写屏幕转到fragment,从fragment转到Jetpack编写屏幕?我用喷气背包组成导航?有什么帮助吗?