FragmentStateAdapter for ViewPager2 notifyItemChanged not working as expected

做~自己de王妃 提交于 2020-08-07 07:53:33

问题


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

回答1:


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



回答2:


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.



回答3:


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.



来源:https://stackoverflow.com/questions/60408236/fragmentstateadapter-for-viewpager2-notifyitemchanged-not-working-as-expected

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!