Jetpack导航中的共享元素从RecyclerView过渡到详细片段 [英] Shared element transition in Jetpack Navigation from RecyclerView to Detail Fragment

查看:603
本文介绍了Jetpack导航中的共享元素从RecyclerView过渡到详细片段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to make a transition with simple animation of shared element between Fragments. In the first fragment I have elements in RecyclerView, in second - exactly the same element (defined in separate xml layout, in the list elements are also of this type) on top and details in the rest of the view. I'm giving various transitionNames for all elements in bindViewHolder and in onCreateView of target fragment I'm reading them and set them to element I want make transition. Anyway animation is not happening and I don't have any other ideas. Here below I'm putting my code snippets from source and target fragments and list adapter:

ListAdapter:

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
}

DetailFragmentLayout:

<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描述了这些元素,由于我没有进行从recyclerView到目标片段的转换,因此它可以正常工作.也许我在这里错了,将transitionName设置为目标片段中的找到的视图?我不知道该怎么做,因为由于recyclerView项的原因,目标包含布局的ID应该是唯一的.

I made also another application using Jetpack navigation, shared elements and elements described by the same layout.xml and it's working since I'm not making transition from recyclerView to target fragment. Maybe I'm wrong here, setting the transitionName to found view in target fragment? I don't know how to make it another way, because the IDs of target included layout should be unique because of recyclerView items.

推荐答案

好吧,我发现输入带有共享元素的动画应该是什么样子: 在DetailFragment(目标)中,您应该在onViewCreated开始运行 postponeEnterTransition()(我的代码可以从onCreateView移到onViewCreated).现在,您有时间使用transitionName签署目标视图元素.在完成加载数据和查看之后,您必须运行 startPostponedEnterTransition().如果不这样做,则ui会冻结,因此您无法在postponeEnterTransition和startPostponedEnterTransition之间进行耗时的操作.

Okay, I found that how should it looks like to have enter animation with shared element: In DetailFragment (Target) you should run postponeEnterTransition() on start onViewCreated (my code from onCreateView can be moved to onViewCreated). Now you have time to sign target view element with transitionName. After you end with loading data and view, you HAVE TO run startPostponedEnterTransition(). If you don't do it, ui would freeze, so you can't do time consuming operations between postponeEnterTransition and startPostponedEnterTransition.

无论如何,现在问题在于返回转换.当然,这是相同的情况-在发布动画之前,您必须重新加载recyclerView.当然,您也可以使用postponeEnterTransition(即使它是返回转换).就我而言,我的列表由LiveData包装.在源代码片段中,生命周期观察者正在检查数据.还有另一个挑战-如何确定是否加载了数据.从理论上讲,您可以使用有用的内联函数recyclerView:

Anyway, now the problem is with return transition. Because of course it's the same situation - you have to reload recyclerView before you release animation. Of course you can also use postponeEnterTransition (even if it's return transition). In my case, I have list wrapped by LiveData. In source fragment lifecycle observer is checking data. There is another challenge - how to determine if data is loaded. Theoretically with recyclerView you can use helpful inline function:

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

}

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

...and in code where you are applying your layout manager and adapter you can use it like this:

recyclerView.afterMeasure { startPostponedEnterTransition() }

它应该确定返回动画何时开始工作(您必须确保recyclerView项目中的transitionNames是否正确,以便过渡可以具有目标视图项目)

it should do the work with determine time when return animation should start (you have to be sure if transitionNames are correct in recyclerView items so transition can have target view item)

这篇关于Jetpack导航中的共享元素从RecyclerView过渡到详细片段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆