簡體   English   中英

Jetpack Navigation 中從 RecyclerView 到 Detail Fragment 的共享元素轉換

[英]Shared element transition in Jetpack Navigation from RecyclerView to Detail Fragment

我正在嘗試使用片段之間共享元素的簡單動畫進行過渡。 在第一個片段中,我在 RecyclerView 中有元素,在第二個片段中 - 完全相同的元素(在單獨的 xml 布局中定義,列表中的元素也是這種類型)在頂部,在視圖的其余部分中有詳細信息。 我正在為 bindViewHolder 和目標片段的 onCreateView 中的所有元素提供各種 transitionNames 我正在閱讀它們並將它們設置為我想要進行過渡的元素。 無論如何動畫沒有發生,我沒有任何其他想法。 下面我將我的代碼片段來自源和目標片段以及列表適配器:

列表適配器:

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

列表片段(來源):

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>

細節片段(目標):

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 描述的元素制作了另一個應用程序,並且它正在工作,因為我沒有從 recyclerView 過渡到目標片段。 也許我在這里錯了,將 transitionName 設置為目標片段中的 found 視圖? 我不知道如何另辟蹊徑,因為目標包含布局的 ID 應該是唯一的,因為 recyclerView 項目。

好的,我發現使用共享元素輸入動畫應該是什么樣子:在 DetailFragment (Target) 中,您應該在 onViewCreated 啟動時運行推遲EnterTransition( ) (我從 onCreateView 中的代碼可以移動到 onViewCreated)。 現在您有時間使用 transitionName 簽署目標視圖元素。 在加載數據和查看結束后,您必須運行startPostponedEnterTransition() 不做的話ui會卡死,所以不能在delayEnterTransition和startPostponedEnterTransition之間做耗時的操作。

無論如何,現在的問題是返回轉換。 因為當然這是同樣的情況——你必須在發布動畫之前重新加載 recyclerView。 當然你也可以使用welcomeEnterTransition(即使是返回轉換)。 就我而言,我有 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() }

它應該確定返回動畫應該開始的時間(您必須確定 recyclerView 項目中的 transitionNames 是否正確,以便過渡可以具有目標視圖項目)

從答案來看,使用 ViewTreeObserver 相當消耗資源。 並且還有很多流程要做。 所以我建議你使用doOnPreDraw而不是在測量 recyclerView 之后等待。 下面的代碼實現會喜歡這個。

recyclerView.doOnPreDraw {
    startPostponedEnterTransition()
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM