Переход общего элемента в Jetpack Navigation от RecyclerView к фрагменту данных - PullRequest
0 голосов
/ 18 октября 2018

Я пытаюсь сделать переход с простой анимацией общего элемента между фрагментами.В первом фрагменте у меня есть элементы в RecyclerView, во втором - точно такой же элемент (определенный в отдельной компоновке xml, элементы списка также относятся к этому типу) сверху и подробности в остальной части представления.Я даю различные transitionNames для всех элементов в bindViewHolder и в onCreateView целевого фрагмента, который я читаю, и устанавливаю их в элемент, который я хочу сделать переходом.В любом случае анимация не происходит, и у меня нет никаких других идей.Ниже я помещаю свои фрагменты кода из исходного и целевого фрагментов и адаптера списка:

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 (Source):

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 (Target):

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.

1 Ответ

0 голосов
/ 22 октября 2018

Хорошо, я обнаружил, что, как должно выглядеть, вводить анимацию с общим элементом: в DetailFragment (Target) вы должны запустить postponeEnterTransition () при запуске onViewCreated (мой код из onCreateView можно переместить вonViewCreated).Теперь у вас есть время подписать целевой элемент view с помощью transitionName.После завершения загрузки данных и просмотра вы ДОЛЖНЫ выполнить startPostponedEnterTransition () .Если вы этого не сделаете, пользовательский интерфейс зависнет, поэтому вы не сможете выполнять трудоемкие операции между postponeEnterTransition и startPostponedEnterTransition.

В любом случае, теперь проблема заключается в обратном переходе.Потому что, конечно, это та же самая ситуация - вы должны перезагрузить recyclerView перед выпуском анимации.Конечно, вы также можете использовать postponeEnterTransition (даже если это обратный переход).В моем случае у меня есть список, обернутый 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, чтобы переход мог иметь целевой элемент вида)

...