Проблема ViewPager2 / Tabs зі станом ViewModel


9

Я дотримуюся схеми MVVM - це означає, що у мене є ViewModel для кожного фрагмента.

Я додав дві вкладки, використовуючи ViewPager2.

Мій адаптер виглядає так:

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

Вкладки працюють. Однак я помітив, що ViewModel мого MergedItemsFragment поводиться дивно. Перш ніж додати вкладки, я перейшов до фрагменту так:

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

Коли я залишив цей фрагмент з NavHostFragment.findNavController(this).popBackStack()і пізніше повернувся до цього фрагмента, я отримав би новий порожній ViewModel. Це було задумано.

З новим підходом я орієнтуюся return new MergedItemsFragment(). Коли я залишаю цей фрагмент і пізніше повертаюсь, я отримую ViewModel, який містить старі дані . Це проблема, оскільки старі дані вже не актуальні, оскільки Користувач вибрав різні дані в іншому фрагменті.


Оновлення №1

Я зрозумів, що він насправді зберігає всі старі фрагменти в пам’яті, оскільки одні й ті ж заяви про друк викликаються кілька разів. Час, який він називається, збільшується із тим, скільки я залишаю і повертаюся на цей екран. Тож якщо я вийду і повернусь 10 разів і поверну свій пристрій, він фактично виконає один рядок 10 разів. Хтось здогадується, як реалізувати вкладки / ViewPagers з навігаційними компонентами таким чином, що працює з ViewModels?


Оновлення №2

Я встановив свої ViewModels таким чином:

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

Я отримую ті ж результати з:

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

Я пов'язую ViewModel у самому Фрагменті. Тому thisє Фрагмент.


Чи можете ви показати, як налаштовуєте ViewModels? Крім того, чи є причина, що ви не можете просто створити новий ViewModel, коли отримаєте нові дані?
BlackHatSamurai

Я оновив своє запитання. Чи не сенс точки зору, що вона сама про це піклується? Я створюю його один раз, і він зберігається на одному фрагменті. Як саме я б відтворив це, якщо у мене з’явилися нові дані і чому я не мав цього робити раніше?
користувач123456789

Вам не потрібно було це робити раніше, оскільки фрагмент був знищений. Тепер ви використовуєте ViewPager, і він зберігає фрагмент у пам'яті. Я б запропонував просто очистити дані, коли потрібно. Вам потрібно керувати даними у ВМ, а не в самій ВМ.
BlackHatSamurai

Проблема, яку я маю, полягає в тому, що старі віртуальні віртуальні машини фактично все ще обслуговують старі LiveData та подають інші компоненти інших даних. Отож очищення даних не обійдеться, оскільки старі вітрини постійно перешкоджають. Приклад: я очищаю список у поточному ViewModel. Однак екран все ще отримує старий список. Коли я налагоджую ViewModel і перевіряю довжину списку, він говорить 0 - оскільки він був очищений. Єдине логічне пояснення - це інші ViewModels, які обслуговують старі дані.
користувач123456789

Ви використовуєте однаковий VM для кожного з фрагментів? Або кожен фрагмент має власний VM?
BlackHatSamurai

Відповіді:


3

Згідно з вашим коментарем, ви використовуєте фрагмент, і всередині цього фрагмента знаходиться ваш знімач. Отже, створюючи адаптер для ViewPager, вам потрібно передавати childFragmentManager замість getActivity ()

Нижче наведено зразок адаптера для вашого viewPager, який ви можете використовувати

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

і під час створення адаптера називайте це так

   val adapter = NewViewPagerAdapter(
        childFragmentManager,
        FragmentPagerAdapter.POSITION_UNCHANGED
    )

як якщо ви бачите документацію для FragmentStatePagerAdapter, вона стверджує, що вам слід пройти (FragmentManager, int) всередині конструктора адаптера

Я сподіваюся, що це вирішить ваше питання, оскільки я одного разу зіткнувся з тим самим питанням.

Щасливе кодування.


1
Дякую. Як вже говорив ianhanniballake, достатньо пропустити сам фрагмент, просто переконайтеся, що у вас є відповідний кондуктор. Тож обидві анвери є правильними.
користувач123456789
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.