7

I am using ViewPager2 with FragmentStateAdapter and i am calling notifyItemChanged(position). But as expected createFragment method not calling again. Is it is the intended behavior or bug, what should I do

My Code is

    class EEditionPagerAdapter(
    fragmentManager: FragmentManager, lifecycle: Lifecycle, private var date: String
    ) : FragmentStateAdapter(fragmentManager, lifecycle) {

    private var menuList = emptyList<EEditionMenu>()

    override fun getItemCount(): Int = menuList.size

    override fun createFragment(position: Int): Fragment {
        val menu = menuList[position]
        return EEditionListingFragment.newInstance(menu, date)
    }

    fun submitList(list: List<EEditionMenu>) {
        menuList = list
        notifyItemRangeChanged(0, menuList.size)
    }

    fun changeDate(date: String, position: Int){
        this.date = date
        notifyItemChanged(position)
    }
}
Karthick Ramanathan
  • 642
  • 3
  • 9
  • 20

3 Answers3

7

FragmentStateAdapter uses item IDs to determine if a fragment has already been created and reuses already created ones. By default, an item ID is the item's position. You can override getItemId and containsItem to provide your own unique IDs based on positions and dates, then it will ask for a new fragment when the date changes.

StenSoft
  • 9,369
  • 25
  • 30
4

When you call notifyItemChanged for FragmentStateAdapter it call ensureFragment(position)that restores the fragment if it has already been created

 private void ensureFragment(int position) {
    long itemId = getItemId(position);
    if (!mFragments.containsKey(itemId)) {
        // TODO(133419201): check if a Fragment provided here is a new Fragment
        Fragment newFragment = createFragment(position);
        newFragment.setInitialSavedState(mSavedStates.get(itemId));
        mFragments.put(itemId, newFragment);
    }
}

Try to override onBindViewHolder and set Date to fragment

   override fun onBindViewHolder(
    holder: FragmentViewHolder,
    position: Int,
    payloads: MutableList<Any>
) {
    super.onBindViewHolder(holder, position, payloads)
    val fragment: EEditionListingFragment? = fragmentManager.findFragmentByTag("f$position") as EEditionListingFragment?
    fragment.updateDate(date)
}
kalugin1912
  • 193
  • 13
0

The fragment put to the mFragment in FragementStateAdapter.class is managed by the

void placeFragmentInViewHolder(@NonNull final FragmentViewHolder holder)

method, which is package private and we do not have access and in this method the fragment is added to the mFragment manager with tag "f" + holder.getItemId() which is the same as "f" + position.

        scheduleViewAttach(fragment, container);
        mFragmentManager.beginTransaction()
                .add(fragment, "f" + holder.getItemId())
                .setMaxLifecycle(fragment, STARTED)
                .commitNow();
        mFragmentMaxLifecycleEnforcer.updateFragmentMaxLifecycle(false);

For this reason, we can override the onBindViewHolder method while a fragement shall be displayed.

@Override
public void onBindViewHolder(@NonNull FragmentViewHolder holder, int position, @NonNull List<Object> payloads) {
    super.onBindViewHolder(holder, position, payloads);
    if (position == 0) {

        String fragmentTag = "f" + holder.getItemId();
        Log.v(TAG,"fragmentTag is : " + fragmentTag);
        MyFragment fragment0 = (MyFragment) this.mFragmentManger.findFragmentByTag(fragmentTag);

        if (fragment0 != null) {
            Log.v(TAG,"onBindViewHolder updating fragment ...");
            // do what ever you want to update the MyFragment 
            fragment0.update(payloads);
        } else {
            Log.v(TAG,"onBindViewHolder called, but fragment0 is null!");
        }
    }
} 

In my case the this.mFragmentManager is cashed through the constructor.

public MyFragmentStateAdapter(@NonNull Fragment fragment) {
        super(fragment); // this calls the fragment.getChildFragmentManager().
        this.mFragmentManger = fragment.getChildFragmentManager();
}

And the createFragment() is:

@NonNull
@Override
public Fragment createFragment(int position) {
    if(position == 0){
        if (this.fragment0 == null) {
            this.fragment0 = MyFragment.getInstance();
        }
        return this.fragment0;
    } else {
        Log.v(TAG, "position " + position + " is called");
        return null;
    }
}

Finally, call the update in somewhere else to trigger the update:

ViewPager2.getAdapter().notifyItemChanged(0, null); 
// 0 is the fragment position of the fragment0 and null is payload.
Yingding Wang
  • 450
  • 3
  • 14