Распределение модели представления по нескольким фрагментам (не активности) с помощью компонента навигации

Я использую компонент навигации, я хочу, чтобы модель представления разделялась между несколькими фрагментами, но они должны быть очищены, когда я оставляю фрагменты (следовательно, не привязываю их к активности). Я пытаюсь взять одно действие, много фрагментов подход. Мне удалось добиться этого, используя несколько узлов навигации и оценивая фрагменты с помощью getParentFragment, но это просто приводит к большему количеству проблем, связанных с переносом фрагментов в другие родительские фрагменты, потерей кнопки возврата, работающей без проблем, и другими хаками, чтобы заставить что-то работать, что должно быть довольно простым. Кто-нибудь знает, как этого добиться? Мне было интересно, есть ли что-нибудь с getViewModelStore, которое я мог бы использовать, учитывая изображение ниже, я хочу охватить модель представления для createCardFragment2 и использовать ее во всем после него (addPredictions, editImageFragment и другие, которые я еще не добавил), но тогда, если Я возвращаюсь к mainFragment. Я хочу очистить модели просмотра.

Кстати, я не могу просто вызвать clear в хранилище модели представления mainFragment, поскольку здесь есть другие модели представления, которые не следует очищать, я думаю, мне нужен способ сообщить навигационному узлу, каким должен быть родительский фрагмент, который, как я знаю, не будет чем-то или способом сделать модель представления новой, если я перехожу из mainFragment или cardPreviewFragment

навигационный график


person martinseal1987    schedule 08.06.2019    source источник
comment
Когда вы инициализируете свой ViewModel с помощью ViewModelProviders, вам необходимо указать контекст. В случае фрагмента вы помещаете this, так что ViewModel жизнь ограничивается фрагментом. ViewModelProviders достаточно умен, чтобы различать действие или контекст фрагмента. В случае регистрации наблюдаемых, вы должны зарегистрировать наблюдателей в onActivityCreated, используя viewLifeCycleOwner в качестве контекста наблюдателя. Это заставляет наблюдателя жить в соответствии с жизненным циклом фрагмента.   -  person Taseer    schedule 08.06.2019
comment
Извините, но я не думаю, что вы понимаете вопрос, я хорошо знаю, как модель представления привязывает свои крючки к жизненному циклу   -  person martinseal1987    schedule 08.06.2019


Ответы (3)


Да, теперь можно привязать модель просмотра к навигационному графу, начиная с androidx.navigation:*:2.1.0-alpha02. См. Примечания к выпуску здесь и пример API здесь. Все, что вам нужно указать, это R.id для вашего навигатора. Я нахожу это немного раздражающим в использовании, потому что обычно модели просмотра инициализируются в onCreate, что невозможно с этой областью видимости, потому что навигационный контроллер еще не гарантированно будет установлен фрагментом вашего навигационного хоста (я нахожу это в случае изменения конфигурации).

Кроме того, если вы не хотите, чтобы ваш mainFragment был частью этой области, я бы предложил вынуть его и, возможно, использовать вложенный навигационный график.

person Alex H    schedule 21.06.2019
comment
Не могли бы вы опубликовать пример того, как вы это используете? В настоящее время я получаю сообщение об ошибке, в котором говорится, что навигационный график не находится в заднем стеке, а это определенно - person martinseal1987; 28.09.2019
comment
Также я пытаюсь получить его через владельца магазина модели представления, который, как мне кажется, заменил магазин модели представления - person martinseal1987; 28.09.2019
comment
Можно ли использовать сохраненный обработчик состояния внутри sharedviewmodel? - person Noah13; 06.01.2020
comment
Если мы не можем инициализировать его в onCreate, где его следует инициализировать? - person Noam; 03.05.2020
comment
подробный ответ с битовым кодом здесь: stackoverflow.com/a/61929726/6341943 - person hushed_voice; 21.05.2020
comment
Есть ли способ узнать, когда произошел запуск и остановка в отношении этой расширенной области? Например, если у вас есть какой-то socketmanager и вы хотите, чтобы он был активен только в этом навигационном графе, но также подключался / отключался, когда происходит последний фрагмент при остановке? - person uberchilly; 02.07.2020

Вот конкретный пример принятого ответа Alex H.

В вашем build.gradle (приложении)

dependencies {
    def nav_version = "2.1.0"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
}

Пример модели просмотра

class MyViewModel : ViewModel() { 
    val name: MutableLiveData<String> = MutableLiveData()
}

В вашем FirstFlowFragment.kt определите

val myViewModel: MyViewModel by navGraphViewModels(R.id.your_nested_nav_id)
myViewModel.name.value = "Cool Name"

И в вашем SecondFlowFragment.kt определите

val myViewModel: MyViewModel by navGraphViewModels(R.id.your_nested_nav_id)
val name = myViewModel.name.value.orEmpty()
Log.d("tag", "welcome $name!")

Теперь ViewModel находится в этом вложенном фрагменте, общее состояние будет уничтожено, когда также будет уничтожена вложенная навигация, нет необходимости вручную сбрасывать их.

person Miko Chu    schedule 18.12.2019
comment
Мне кажется странным, что ваш ViewModel реализован (расширяет) _2 _... конечно, viewmodel будет ограничен до fragment: это один и тот же объект .... Я не знаю, упустил ли я что-то из вашего решение... - person GaRRaPeTa; 18.12.2019
comment
Оооо, извини, что написал очень быстро, исправил. - person Miko Chu; 18.12.2019
comment
В этом больше смысла. - person GaRRaPeTa; 18.12.2019
comment
Надеюсь, мой пример окажется для вас полезным! - person Miko Chu; 18.12.2019
comment
Можно ли использовать сохраненный обработчик состояния внутри sharedviewmodel? - person Noah13; 06.01.2020
comment
Вот статья для Ноя: medium.com/@elye .project / - person Miko Chu; 08.01.2020
comment
Необходимо добавить defaultViewModelProviderFactory с помощью Hilt. Например: private val viewModel: SpaceListViewModel by navGraphViewModels(R.id.spaceFragment) { defaultViewModelProviderFactory } - person Sai; 16.05.2021

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

с использованием

//Navigation
implementation "androidx.navigation:navigation-fragment:2.2.0-rc04"
// Navigation UI
implementation "androidx.navigation:navigation-ui:2.2.0-rc04"

я получаю владельца магазина модели представления вот так

private ViewModelStoreOwner getStoreOwner() {

        NavController navController = Navigation
                .findNavController(requireActivity(), R.id.root_navigator_fragment);
        return navController.getViewModelStoreOwner(R.id.root_navigator);
}

Я использую реализацию нескольких фрагментов одного действия, но с помощью этого я могу эффективно связать свои модели представления только с ограниченными фрагментами и с новым живые данные, вы также можете ограничить это

первый идентификатор взят из фрагмента навигационных графиков

<?xml version="1.0" encoding="utf-8"?>
  <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <fragment
      android:id="@+id/root_navigator_fragment"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:name="androidx.navigation.fragment.NavHostFragment"
      app:defaultNavHost="true"
      app:navGraph="@navigation/root_navigator"/>

  </FrameLayout>

а второй происходит от идентификатора навигационного графа

  <?xml version="1.0" encoding="utf-8"?>
  <navigation 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:id="@+id/root_navigator"
    app:startDestination="@id/mainNavFragment">

а затем вы можете использовать его так

private void setUpSearchViewModel() {
    searchViewModel = new ViewModelProvider(getStoreOwner()).get(SearchViewModel.class);
}
person martinseal1987    schedule 05.03.2020