Problem ViewPager2 / Tabs ze stanem ViewModel

9

Podążam za wzorcem MVVM - co oznacza, że ​​mam ViewModel dla każdego fragmentu.

Dodałem dwie zakładki za pomocą ViewPager2.

Mój adapter wygląda następująco:

@Override
public Fragment createFragment(int position) {
    switch (position) {
        case 0:
            return new MergedItemsFragment();
        case 1:     
            return new ValidatedMergedItemsFragment();
    }
    return new MergedItemsFragment();
}

Karty działają. Zauważyłem jednak, że ViewModel mojego MergedItemsFragment zachowuje się dziwnie. Przed dodaniem zakładek nawigowałem do Fragmentu w następujący sposób:

NavHostFragment.findNavController(this).navigate(R.id.action_roomFragment_to_itemsFragment);

Kiedy opuściłem ten fragment, NavHostFragment.findNavController(this).popBackStack()a później wróciłem do tego fragmentu, dostanę nowy pusty ViewModel. To było zamierzone.

Dzięki nowemu podejściu nawiguję return new MergedItemsFragment(). Kiedy opuszczam ten fragment, a później wracam, otrzymuję ViewModel, który zawiera stare dane . Jest to problem, ponieważ stare dane nie są już istotne, ponieważ użytkownik wybrał różne dane w innym fragmencie.


Aktualizacja nr 1

Uświadomiłem sobie, że tak naprawdę zachowuje wszystkie stare Fragmenty w pamięci, ponieważ te same instrukcje drukowania są wywoływane wiele razy. Czas nazywa się tym wzrasta wraz z czasem, kiedy wychodzę i wracam do tego ekranu. Więc jeśli wyjdę i wrócę 10 razy i obrócę swoje urządzenie, faktycznie wykona jedną linię 10 razy. Czy zgadujesz, jak zaimplementować Tab / ViewPagers za pomocą komponentów nawigacyjnych w sposób, który działa z ViewModels?


Aktualizacja nr 2

Moje ViewModels ustawiam w następujący sposób:

viewModel = new ViewModelProvider(this, providerFactory).get(MergedItemViewModel.class)

Otrzymuję te same wyniki z:

viewModel = ViewModelProviders.of(this).get(MergedItemViewModel.class);

Wiążę ViewModel w samym fragmencie. Dlatego thisjest Fragment.

użytkownik123456789
źródło
Czy możesz pokazać, jak ustawiasz swoje modele ViewModels? Ponadto, czy jest jakiś powód, dla którego nie można po prostu utworzyć nowego ViewModel po otrzymaniu nowych danych?
BlackHatSamurai
Zaktualizowałem moje pytanie. Czy nie jest sensem modelu widzenia, że ​​sam się tym zajmuje? Tworzę go raz i trwa dla jednego fragmentu. Jak dokładnie miałbym go odtworzyć na wypadek, gdyby miałem nowe dane i dlaczego nie musiałem tego robić wcześniej?
user123456789
Nie trzeba było tego robić wcześniej, ponieważ fragment został zniszczony. Teraz używasz ViewPager i przechowuje fragment w pamięci. Proponuję po prostu wyczyścić dane, kiedy zajdzie taka potrzeba. Musisz zarządzać danymi na maszynie wirtualnej, a nie na samej maszynie wirtualnej.
BlackHatSamurai
Problem, jaki mam, polega na tym, że stare maszyny wirtualne nadal obsługują stare dane LiveData i zasilają pozostałe komponenty starymi danymi. Tak więc wyczyszczenie danych nie przyniesie rezultatu, ponieważ stare maszyny wirtualne nadal się wtrącają. Przykład: Wyczyściłem listę w bieżącym ViewModel. Jednak ekran nadal otrzymuje starą listę. Kiedy debuguję ViewModel i sprawdzam długość listy, mówi 0 - ponieważ została wyczyszczona. Jedynym logicznym wyjaśnieniem są inne ViewModels obsługujące stare dane.
user123456789
Czy używasz tej samej maszyny wirtualnej dla każdego z fragmentów? Czy każdy fragment ma własną maszynę wirtualną?
BlackHatSamurai

Odpowiedzi:

3

Zgodnie z komentarzem używasz Fragmentu, a wewnątrz tego Fragmentu znajduje się Twoja przeglądarka. Dlatego podczas tworzenia adaptera do ViewPager musisz przekazać childFragmentManager zamiast getActivity ()

Poniżej znajduje się przykładowy adapter do przeglądarki viewPager, którego można użyć

class NewViewPagerAdapter(fm: FragmentManager, behavior: Int) : FragmentStatePagerAdapter(fm, behavior) {
    private val mFragmentList: MutableList<Fragment> = ArrayList()
    private val mFragmentTitleList: MutableList<String> = ArrayList()

    override fun getItem(position: Int): Fragment {
        return mFragmentList[position]
    }

    override fun getCount(): Int {
        return mFragmentList.size
    }

    fun addFragment(fragment: Fragment, title: String) {
        mFragmentList.add(fragment)
        mFragmentTitleList.add(title)
    }

    override fun getPageTitle(position: Int): CharSequence? {
        return mFragmentTitleList[position]
    }
}

i podczas tworzenia adaptera nazwij to tak

   val adapter = NewViewPagerAdapter(
        childFragmentManager,
        FragmentPagerAdapter.POSITION_UNCHANGED
    )

tak jak w dokumentacji FragmentStatePagerAdapter stwierdza się, że należy przekazać (FragmentManager, int) do konstruktora adaptera

Mam nadzieję, że to rozwiąże problem, ponieważ pewnego dnia miałem do czynienia z tym samym problemem.

Szczęśliwego kodowania.

Rakshit Nawani
źródło
1
Dzięki. Jak już powiedział ianhanniballake, wystarczy przekazać sam fragment, po prostu upewnij się, że masz odpowiedniego wykonawcę. Więc obie odpowiedzi są poprawne.
user123456789