Как привязать модель представления к родительскому фрагменту?

Итак, я использую новый компонент навигации (с принципом одного действия) и общаюсь между каждым фрагментом с использованием общих моделей представления, однако я дошел до точки, когда мне иногда нужно очистить модель представления, но я не могу найти хорошее место, чтобы его очистить. Но, черт возьми, я думаю, что вместо того, чтобы пытаться очистить это сам, я действительно должен позволить фреймворку делать это за меня, но это не так, потому что модели представления являются общими и привязаны к деятельности, но я думаю, что могу охватить их до родительский фрагмент, я сделал рисунок, чтобы проиллюстрировать, что я пытаюсь сделать.  my navigation flow , поэтому я хочу очистить только 2 модели представления, когда я возвращаюсь из" Child 1 Child a "в настоящее время модели представления никогда не очищаются, пытаясь получить модель представления в настоящее время, вызывая 'this' во фрагменте, а getParentFragment в дочернем элементе не работает, может ли кто-нибудь предоставить пример?

ИЗМЕНИТЬ

Похоже, я уже делал что-то подобное, но в моем случае это не работает, поэтому я добавлю код, вот как я получаю доступ к первой модели представления в родительском фрагменте

model = ViewModelProviders.of(this).get(RequestViewModel.class);

а затем в дочернем фрагменте я делаю это

requestViewModel = ViewModelProviders.of(getParentFragment()).get(RequestViewModel.class);

но он не хранит данные между ними, у них обоих есть прикрепленные наблюдатели


person martinseal1987    schedule 17.12.2018    source источник
comment
Вы можете обратиться к этому ответу stackoverflow.com/a/52732831/10271334, дайте мне знать, если возникнет какая-то путаница.   -  person Jeel Vankhede    schedule 17.12.2018
comment
хорошо, это то, что я уже пробовал, позвольте мне добавить код   -  person martinseal1987    schedule 17.12.2018
comment
Я не уверен, что мне что-то не хватает, но вы можете полностью зарегистрировать ViewModel с конкретным экземпляром фрагмента, чтобы вам не пришлось беспокоиться о его возвращении для фрагмента.   -  person cincy_anddeveloper    schedule 17.12.2018
comment
@WadeWilson Я добавил в свой код то, что я делаю, инициализирую модели представления, но данные не передаются между двумя   -  person martinseal1987    schedule 17.12.2018
comment
После прочтения вашей ревизии, если вы хотите, чтобы два фрагмента, родительский и дочерний, имели свой собственный экземпляр RequestViewModel, уникальный для них, не вызывайте getParentFragment () в дочернем фрагменте. если вы хотите поделиться моделью просмотра, передаваемая вами ссылка на объект должна быть идентична. Я думаю, вам может потребоваться убедиться, что вы передаете тот же экземпляр в вызов .of (...).   -  person cincy_anddeveloper    schedule 17.12.2018
comment
@WadeWilson нет, я бы хотел, чтобы данные между ними были одинаковыми, поэтому используйте один и тот же экземпляр модели представления   -  person martinseal1987    schedule 17.12.2018
comment
Если вы хотите поделиться Viewmodel, передаваемая вами ссылка на объект должна быть идентична. Я думаю, вам может потребоваться убедиться, что вы передаете тот же экземпляр в вызов .of (...). Либо выполните отладку, либо измените метод toString, чтобы вернуть уникальное значение и выйти из значения, которое вы передаете в ViewModelProviders.of (...) как внутри родительского, так и дочернего фрагмента.   -  person cincy_anddeveloper    schedule 17.12.2018
comment
@WadeWilson, я сделал то, что вы предложили, и получил эти RaiseRequestFragment {9afdd34 # 1 id = 0x7f0a0133} NavHostFragment {1333143 # 0 id = 0x7f0a0133}, поэтому идентификаторы совпадают   -  person martinseal1987    schedule 17.12.2018
comment
nav host - это не фрагмент, который его вызывает, но это фрагмент, который содержит мой навигационный граф, поэтому я предполагаю, что это родитель, но идентификатор из фрагмента, который его вызывает   -  person martinseal1987    schedule 17.12.2018
comment
Я не уверен, что вы сделали то же самое, потому что я не вижу ваш код. Экземпляр фрагмента, переданный в ViewModelsProvider.of (...), должен быть идентичным как в родительском, так и в дочернем фрагменте. Я знаю, что вы можете совместно использовать экземпляры ViewModels между фрагментами, передав ссылку на одно и то же родительское действие. Вы пробовали это делать? Просмотрите эту ссылку для получения дополнительных сведений: developer.android.com/topic/libraries/architecture /   -  person cincy_anddeveloper    schedule 17.12.2018
comment
Раньше я использовал это действие для достижения этой цели, и да, это работало нормально, после попытки изменить это на область фрагмента, она больше не работала   -  person martinseal1987    schedule 17.12.2018
comment
Один вопрос @martin: добавляли / заменяли дочерние фрагменты с помощью диспетчера дочерних фрагментов или только с помощью диспетчера фрагментов из родительского фрагмента?   -  person Jeel Vankhede    schedule 17.12.2018
comment
@JeelVankhede также я не использую новый компонент архитектуры навигации, который, как я предполагаю, будет использовать диспетчер фрагментов при вызове из действий и диспетчер дочерних фрагментов при вызове из фрагмента   -  person martinseal1987    schedule 17.12.2018
comment
Можете ли вы попробовать распечатать или отладить хэш-код для вашего общего ViewModels в родительском и дочернем фрагментах, просто чтобы убедиться, что они общие? Если они оба имеют одинаковое значение, они общие ... также проверьте дочерние фрагменты. Это может быть причиной.   -  person Jeel Vankhede    schedule 17.12.2018
comment
@JeelVankhede у них одинаковый идентификатор D / RSRQSTFRGMNT: идентификатор родительского фрагмента 2131362099 D / RqstCatFrag: идентификатор дочернего фрагмента 2131362099   -  person martinseal1987    schedule 17.12.2018
comment
@JeelVankhede Я добавил свой ответ, поскольку нашел, что это было, но по сути это ваш ответ, если вы хотите добавить его, я с радостью приму   -  person martinseal1987    schedule 17.12.2018


Ответы (9)


Используя Fragment-ktx libr в своем приложении, вы можете получить viewModel, как показано ниже, в вашей сборке приложения. Gradle

 implementation 'androidx.fragment:fragment-ktx:1.1.0'

// получить ViewModel в ParentFragment как

 private val viewModel: DemoViewModel by viewModels()

// получаем тот же экземпляр ViewModel в ChildFragment, что и

 private val viewModel: DemoViewModel by viewModels(
    ownerProducer = { requireParentFragment() }
)
person Alok Mishra    schedule 23.07.2020
comment
В настоящее время есть ошибка с этим при привязке рукояти к компоненту навигации, вам нужно указать фабрику модели представления по умолчанию, иначе это будет подход defacto - person martinseal1987; 23.07.2020
comment
@ martinseal1987 Я обновил изменения по вашему запросу. viewModels импортирует androidx.fragment.app.viewModels, который является частью Fragment-ktx. Спасибо - person Alok Mishra; 23.07.2020

Хорошо, так что используя это в родительском

model = ViewModelProviders.of(this).get(RequestViewModel.class);

и это в ребенке

requestViewModel = ViewModelProviders.of(getParentFragment()).get(RequestViewModel.class);

давали мне разные хэш-коды, но одинаковые идентификаторы, и, похоже, из-за компонента навигации, если я изменю их оба на getParentFragment, тогда он будет работать, поэтому я думаю, что компонент заменяет фрагменты вместо того, чтобы добавлять их сюда, большое спасибо @WadeWilson и @JeelVankhede

person martinseal1987    schedule 17.12.2018
comment
Что касается наблюдения, что мы отправляем как владелец жизненного цикла? Я получил Unsafe call to observe with Fragment instance as LifecycleOwner from MyFragment.onViewCreated. - person Bagus Aji Santoso; 09.08.2020
comment
используйте viewLifecycleOwner, pokemonListViewModel.searchPokemon.observe (viewLifecycleOwner, Observer {pokemonList - › - person martinseal1987; 09.08.2020
comment
Поэтому я использую метод viewModel: by viewModels для ленивого создания viewModel. И для того, чтобы использовать одну и ту же модель представления между фрагментом и запущенным bottomSheetFragmentDialog, мне нужно использовать `by viewModels ({requireParentFragment ()}) как во фрагменте, так и в bottomSheetFragment. - person dumbfingers; 22.12.2020

Итак, согласно предлагаемому решению @martin, даже если добавлен один / несколько фрагментов будучи дочерним внутри родительского фрагмента, компонент навигации предоставляет один и тот же диспетчер фрагментов для обоих фрагментов.

Это означает, что даже если фрагменты добавлены как иерархия родитель-потомок, они будут использовать один и тот же диспетчер фрагментов из компонента навигации (может быть ошибка в этой библиотеке?) &, так что ViewModels не будут использоваться совместно из-за этой дилеммы при использовании экземпляра getParentFragment() для ViewModelProvider внутри дочернего фрагмента.


Итак, одним быстрым решением для совместного использования ViewModels было бы получение экземпляра родительского фрагмента из диспетчера фрагментов, используя строку ниже для родительского и дочернего фрагментов:

ViewModelProviders.of(getParentFragment()).get(SharedViewModel.class); // using this in both parent amd child fragment would do the trick !
person Jeel Vankhede    schedule 17.12.2018
comment
есть еще кое-что, если вы объявите фрагмент как фрагмент в xml и предоставите ему график в xml, тогда этот фрагмент будет родительским фрагментом, что означает, что я могу отделить часть моего потока навигации, но, к сожалению, это иногда нарушает ваши вызовы popBackStack, потому что в конечном итоге вам понадобится контейнер для хранения фрагмента, я НЕНАВИЖУ компонент навигации! так как я рекомендую не использовать, так как я считаю, что он сломан, но моя компания хочет, чтобы я использовал его, поскольку он должен быть фактическим путем в будущем, еще раз спасибо - person martinseal1987; 18.12.2018
comment
Да, на самом деле рекомендуется, чтобы, если какая-либо библиотека или зависимость находится в альфа-версии, избегайте или старайтесь не использовать ее до тех пор, пока она не станет стабильной; ради конечной стабильной работы. - person Jeel Vankhede; 18.12.2018
comment
вы можете исправить кнопку возврата, вызвав getActivity (). getSupportFragmentManager (). popBackStack () - person martinseal1987; 19.12.2018
comment
getParentFragment() не вернет null, если это родительский фрагмент. В таком случае этот метод вызовет исключение? - person Archie G. Quiñones; 21.02.2019
comment
Нет, объект будет просто нулевым. Этот фрагмент кода не вызовет никаких исключений. - person Jeel Vankhede; 21.02.2019

У меня такая же проблема, как и все решение, но не работающая в моем сценарии, я предлагаю другое решение с использованием requireParentFragment () return NavHostFragment в дочернем фрагменте, решите его, используя

private val viewModel: childViewModel by viewModels(
            ownerProducer = { requireParentFragment().childFragmentManager.primaryNavigationFragment!! }
    )

в Parent Fragmet используйте это

private val viewModel: MyOrdersVM by viewModels()
person Abdul Wahab    schedule 28.01.2021

Если вы используете компонент навигации (https://developer.android.com/guide/navigation ), вам нужно получить viewModel следующим образом:

Реализуйте fragment-ktx в своем приложении - ›build.gradle:

implementation 'androidx.fragment:fragment-ktx:1.2.5

В родительском фрагменте:

private val viewModel by viewModels<ParentFragmentViewModel>()

В дочернем фрагменте

private val viewModel by viewModels<ParentFragmentViewModel>({requireGrandParentFragment()})

requireGrandParentFragment () - это настраиваемое расширение Fragment:

fun Fragment.requireGrandParentFragment() = this.requireParentFragment().requireParentFragment()

Причина, по которой вам нужно подняться на два уровня для доступа к viewModel parentFragment, заключается в том, что первым родительским элементом childFragment в компоненте навигации является NavHostFragment, а родительским элементом NavHostFragment является parentFragment, где находится модель представления.

Если вы не используете компонент Navigation, вы можете получить к нему доступ следующим образом в childFragment:

private val viewModel by viewModels<ParentFragmentViewModel>({requireParentFragment()})
person Ndriçim Sadiku    schedule 06.01.2021

Google предоставил нам возможность использовать ViewModel для навигационных графиков. Вы можете использовать его, если уже используете компонент навигации. (Личное мнение, вам СЛЕДУЕТ перейти к компоненту навигации, если вы еще не используете его, так как он очень прост в использовании. Взять его у парня, который сам пытался управлять обратным стеком)

Вы можете выбрать все фрагменты, которые необходимо сгруппировать внутри навигационного графа, и right-click->move to nested graph->new graph

теперь это переместит выбранные фрагменты во вложенный граф внутри основного навигационного графа следующим образом:

<navigation app:startDestination="@id/homeFragment" ...>
    <fragment android:id="@+id/homeFragment" .../>
    <fragment android:id="@+id/productListFragment" .../>
    <fragment android:id="@+id/productFragment" .../>
    <fragment android:id="@+id/bargainFragment" .../>
    
    <navigation 
        android:id="@+id/checkout_graph" 
        app:startDestination="@id/cartFragment">

        <fragment android:id="@+id/orderSummaryFragment".../>
        <fragment android:id="@+id/addressFragment" .../>
        <fragment android:id="@+id/paymentFragment" .../>
        <fragment android:id="@+id/cartFragment" .../>

    </navigation>
    
</navigation>

Теперь внутри фрагментов при инициализации ViewModel сделайте это

val viewModel: CheckoutViewModel by navGraphViewModels(R.id.checkout_graph)

Если вам нужно передать фабрику модели просмотра (может быть, для внедрения модели просмотра), вы можете сделать это следующим образом:

val viewModel: CheckoutViewModel by navGraphViewModels(R.id.checkout_graph) { viewModelFactory }

Убедитесь, что это R.id.graph, а не R.navigation.graph

По какой-то причине создание навигационного графа и использование include для его вложения в основной навигационный граф у меня не работало. Наверное, это ошибка.

Источник: https://medium.com/androiddevelopers/viewmodels-with-saved-state-jetpack-navigation-data-binding-and-coroutines-df476b78144e

Спасибо, очистить Android ViewModel вручную? за то, что указали мне правильное направление.

person hushed_voice    schedule 21.05.2020
comment
полностью согласен, я задал другой вопрос, который требует такого же ответа здесь stackoverflow.com/questions/56505455/ - person martinseal1987; 21.05.2020

версия с использованием Kotlin и отложенной инициализации (AKA by viewModels)

в родительском фрагменте (например, ViewPager2)

private val viewModel: MyViewModel by viewModels({ this }) {
    TODO("MyViewModelFactory instance")
}

в дочернем фрагменте (например, на странице ViewPager2)

private val viewModel: MyViewModel by viewModels({ requireParentFragment() })
person Stachu    schedule 28.12.2020

Что я делал неправильно, так это указывал неверный менеджер фрагментов DialogFragment. В моем случае это работает так:

class MyDialog : DialogFragment() {

    private val viewModel: MyViewModel by viewModels({ requireParentFragment() })

И инициализация диалога в моем fragment:

private fun showDialog(){
    MyDialog().show(childFragmentManager, "AddFriendToGroupDialog")
}

И я использую implementation 'androidx.fragment:fragment-ktx:1.3.0' и навигационные графики.

person Peter Z.    schedule 23.03.2021

У меня тоже была эта пробема. Для дочернего фрагмента была создана новая ViewModel. Проблема в том, что getParentFragment () возвращает NavHostFragment вместо желаемого фрагмента.

И нам нужно получить настоящий родительский объект внутри дочернего фрагмента.

Если мы сделаем это requireParentFragment (). RequireParentFragment (), то получим настоящего родителя.

Решение: (для дочернего обновления)

Fragment parent = requireParentFragment().requireParentFragment();
viewModel = new ViewModelProvider(parent).get(RequestViewModel.class);

Здесь найдено решение

person Михаил Юришинец    schedule 14.04.2021