FragmentStateAdapter not calling createFragment when notifyDataSetChanged is called

Viewed 1999

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?

1 Answers

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++ }
}
Related