ViewPager2 / Tabs-Problem mit dem ViewModel-Status

9

Ich folge dem MVVM-Muster - was bedeutet, dass ich für jedes Fragment ein ViewModel habe.

Ich habe zwei hinzugefügt mit ViewPager2 Registerkarten hinzugefügt.

Mein Adapter sieht folgendermaßen aus:

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

Die Registerkarten funktionieren. Ich habe jedoch festgestellt, dass sich das ViewModel meines MergedItemsFragment merkwürdig verhält. Bevor ich Registerkarten hinzufügte, navigierte ich wie folgt zum Fragment:

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

Wenn ich dieses Fragment mit diesem Fragment verließ NavHostFragment.findNavController(this).popBackStack()und später zu diesem Fragment zurückkehrte, erhielt ich ein neues leeres ViewModel. Dies war beabsichtigt.

Mit dem neuen Ansatz navigiere ich return new MergedItemsFragment(). Wenn ich dieses Fragment verlasse und später zurückkehre, erhalte ich ein ViewModel, das die alten Daten enthält . Dies ist ein Problem, da die alten Daten nicht mehr relevant sind, weil der Benutzer andere Daten in einem anderen Fragment ausgewählt hat.


Update Nr. 1

Mir wurde klar, dass er tatsächlich alle alten Fragmente im Speicher behält, weil dieselben Druckanweisungen mehrmals aufgerufen werden. Die Zeiten, die es heißt, erhöhen sich mit der Häufigkeit, mit der ich diesen Bildschirm verlasse und zurückkehre. Wenn ich also 10 Mal gehe und zurückkehre und mein Gerät drehe, führt er tatsächlich 10 Mal eine Zeile aus. Irgendwelche Vermutungen, wie Tabs / ViewPager mit Navigationskomponenten auf eine Weise implementiert werden können, die mit ViewModels funktioniert?


Update Nr. 2

Ich habe meine ViewModels folgendermaßen eingestellt:

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

Ich erhalte die gleichen Ergebnisse mit:

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

Ich binde das ViewModel im Fragment selbst. Daher thisist das Fragment.

user123456789
quelle
Können Sie zeigen, wie Sie Ihre ViewModels einstellen? Gibt es einen Grund, warum Sie nicht einfach ein neues ViewModel erstellen können, wenn Sie neue Daten erhalten?
BlackHatSamurai
Ich habe meine Frage aktualisiert. Ist es nicht der Sinn eines Ansichtsmodells, dass es sich selbst darum kümmert? Ich erstelle es einmal und es bleibt für ein Fragment bestehen. Wie genau würde ich es neu erstellen, falls ich neue Daten habe und warum musste ich das vorher nicht tun?
user123456789
Sie mussten es vorher nicht tun, weil das Fragment zerstört wurde. Jetzt verwenden Sie einen ViewPager, der das Fragment im Speicher speichert. Ich würde vorschlagen, nur die Daten zu löschen, wenn Sie müssen. Sie müssen die Daten in der VM und nicht in der VM selbst verwalten.
BlackHatSamurai
Das Problem, das ich habe, ist, dass die alten VMs tatsächlich noch alte LiveData bereitstellen und die alten Daten der anderen Komponenten einspeisen. Das Löschen der Daten reicht also nicht aus, da die alten VMs immer wieder stören. Beispiel: Ich lösche eine Liste im aktuellen ViewModel. Der Bildschirm erhält jedoch immer noch die alte Liste. Wenn ich das ViewModel debugge und die Länge der Liste überprüfe, steht dort 0 - da es gelöscht wurde. Die einzig logische Erklärung sind andere ViewModels, die alte Daten bereitstellen.
user123456789
Verwenden Sie für jedes der Fragmente dieselbe VM? Oder hat jedes Fragment eine eigene VM?
BlackHatSamurai

Antworten:

3

Gemäß Ihrem Kommentar verwenden Sie Fragment und in diesem Fragment befindet sich Ihr Viewpager. So , während die Adapter für ViewPager Erstellen müssen Sie passieren childFragmentManager statt getActivity ()

Unten finden Sie einen Beispieladapter für Ihren viewPager, den Sie verwenden können

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

und während Sie Ihren Adapter erstellen, nennen Sie es wie

   val adapter = NewViewPagerAdapter(
        childFragmentManager,
        FragmentPagerAdapter.POSITION_UNCHANGED
    )

Als ob Sie die Dokumentation für FragmentStatePagerAdapter sehen , heißt es, dass Sie (FragmentManager, int) im Konstruktor Ihres Adapters übergeben sollten

Ich hoffe, dies wird Ihr Problem lösen, da ich eines Tages mit demselben Problem konfrontiert war.

Viel Spaß beim Codieren.

Rakshit Nawani
quelle
1
Vielen Dank. Wie ianhanniballake bereits sagte, reicht es aus, das Fragment selbst zu übergeben. Stellen Sie nur sicher, dass Sie einen geeigneten Konstruktor haben. Beide Antworten sind also richtig.
user123456789