4

I am using a ViewPager2 together with a FragmentStateAdapter.

public class MyAdapter extends FragmentStateAdapter {

    public MyAdapter(@NonNull Fragment fragment) {
        super(fragment);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        return MyFragment.newInstance(MyClass.func(position));
    }

    @Override
    public int getItemCount() {
        return MyClass.NUM;
    }

}

Unfortunately, calling notifyDataSetChanged() on the adapter has no effect at all. (Calling notifyItemChanged(position) works fine.)

How can I solve this?

Adil Hussain
  • 30,049
  • 21
  • 112
  • 147
MaxxOr
  • 55
  • 2
  • 6

3 Answers3

6

I've created a bug report for this here in IssueTracker. For me, though, even the notifyItemChanged(position:) and notifyItemRangeChanged(positionStart:itemCount:) methods behave incorrectly.

The bug is due to the default implementation of the FragmentStateAdapter.getItemId(position:) method which simply returns the position passed into it. The itemId for each position will therefore be the same before and after a FragmentStateAdapter.notifyDataSetChanged() call and therefore none of the Fragments will ever be recreated.

One possible workaround is to assign each page a unique id within your FragmentStateAdapter implementation and to assign a new id to each page whenever the notifyDataSetChanged() method is called. See the init block in the ViewPagerAdapter class below which facilitates this workaround: An AdapterDataObserver object is registered in this init block such that it receives a callback whenever notifyDataSetChanged() is called.

class ViewPagerAdapter(fragmentActivity: FragmentActivity) :
    FragmentStateAdapter(fragmentActivity) {

    private var counter = 0L
    private var itemIds: List<Long>

    init {
        itemIds = generateListOfItemIds()

        val adapterDataObserver = object : AdapterDataObserver() {

            override fun onChanged() {
                itemIds = generateListOfItemIds()
            }
        }

        registerAdapterDataObserver(adapterDataObserver)
    }

    override fun createFragment(position: Int): Fragment {
        return PageFragment.newInstance(position)
    }

    override fun getItemCount(): Int {
        return itemIds.size
    }

    override fun getItemId(position: Int): Long {
        return itemIds[position]
    }

    override fun containsItem(itemId: Long): Boolean {
        return itemIds.contains(itemId)
    }

    private fun generateListOfItemIds() = (1..5).map { counter++ }
}
Adil Hussain
  • 30,049
  • 21
  • 112
  • 147
  • See [here](https://github.com/adil-hussain-84/ViewPager2-experiments/tree/master/app2) for a minimal Android application which demonstrates this `ViewPagerAdapter` class. – Adil Hussain Nov 09 '22 at 17:31
4

Before setAdapter() to your viewpager. Make viewPager.setSaveFromParentEnabled(false);

0

Just override fun getItemId(position: Int): Long method on FragmentStateAdapter

Andrew Evtukhov
  • 423
  • 1
  • 6
  • 17