Android NotifyDataSetChanged无法在具有Viewpager2的FragmentStateAdapter中工作

Android NotifyDataSetChanged无法在具有Viewpager2的FragmentStateAdapter中工作,android,android-fragments,android-viewpager,fragmentstateadapter,Android,Android Fragments,Android Viewpager,Fragmentstateadapter,我正在使用带有FragmentStateAdapter的viewpager2创建文件选择页面。在其中一个页面中,我显示了所有装载的存储设备,我希望点击这些设备查看内容,但我希望内容位于另一个片段上 我想用viewpager最后一页中的另一个片段替换该片段。但是使用下面的代码,页面只是刷新,新片段不会被创建,因为createFragment不会被调用 我的寻呼机 public static class PagerAdapter extends FragmentStateAdapter {

我正在使用带有FragmentStateAdapter的viewpager2创建文件选择页面。在其中一个页面中,我显示了所有装载的存储设备,我希望点击这些设备查看内容,但我希望内容位于另一个片段上

我想用viewpager最后一页中的另一个片段替换该片段。但是使用下面的代码,页面只是刷新,新片段不会被创建,因为
createFragment
不会被调用

我的寻呼机

public static class PagerAdapter extends FragmentStateAdapter {


        private Fragment mFragmentAtPos0;
        private final FragmentManager mFragmentManager;
        private final FirstPageListener listener = new FirstPageListener();
        public final class FirstPageListener implements
                FirstPageFragmentListener {
            public void onSwitchToNextFragment() {
                mFragmentManager.beginTransaction().remove(mFragmentAtPos0)
                        .commit();
                if (mFragmentAtPos0 instanceof FilesandFolder_Others_MainPage){
                    mFragmentAtPos0 = new FileExplorer(listener);
                }else{ // Instance of NextFragment
                    mFragmentAtPos0 = new FilesandFolder_Others_MainPage(listener);
                }
                notifyDataSetChanged();
            }
            public void onSwitchToNextFragment(Bundle bundle) {
//                mFragmentManager.beginTransaction().remove(mFragmentAtPos0)
//                        .commit();
                if (mFragmentAtPos0 instanceof FilesandFolder_Others_MainPage){
                    mFragmentAtPos0 = new FileExplorer(listener);
                    mFragmentAtPos0.setArguments(bundle);
                }else{ // Instance of NextFragment
                    mFragmentAtPos0 = new FilesandFolder_Others_MainPage(listener);
                    mFragmentAtPos0.setArguments(bundle);
                }
                notifyDataSetChanged();
//                notifyItemChanged(getItemPosition(mFragmentAtPos0));
            }
        }


//        @Override
//        public void onBindViewHolder(@NonNull FragmentViewHolder holder, int position, @NonNull List<Object> payloads) {
//            super.onBindViewHolder(holder, position, payloads);
//            if (position == getItemPosition(mFragmentAtPos0)) {
//
//                Fragment f = mFragmentManager.findFragmentByTag("f" + holder.getItemId());
//                if (f != null) {
//                    mFragmentManager.beginTransaction().replace(holder.getItemId(), )
//                }
//            }
//        }

        private int getItemPosition(Fragment mFragmentAtPos0) {
            for (int i = 0; i < getItemCount(); i++){

                if (createFragment(i).equals(mFragmentAtPos0)){
                    return i;
                }
            }
            return -1;
        }


        public PagerAdapter(FragmentActivity fm) {
            super(fm);
            mFragmentManager = fm.getSupportFragmentManager();
            mFragmentAtPos0 = new FilesandFolder_Others_MainPage(listener);
        }

        @NonNull
        @Override
        public Fragment createFragment(int position) {
            switch (position){
                case 0:
                    return new AppSelectionFragment();
                case 1:
                    return new Photos();
                case 2:
                    return new VideoGalleryFragment();
                case 3:
                    return new Gallery();
                default:
                    return mFragmentAtPos0;
            }
        }

        @Override
        public int getItemCount() {
            return 5;
        }
    }

点击一个视图, 应调用以下代码:-

Bundle bundle = new Bundle();
bundle.putString("PATH", volumes.get(position).path);
pageFragmentListener.onSwitchToNextFragment(bundle);
我搜索了stackoverflow的所有地方,但从未看到viewpager 2和FragmentStateAdapter的正确答案,因为每个人似乎都在使用viewpager

我试图实现这一点:

但我不明白如何编辑我的代码。请帮忙
提前感谢

也有同样的问题,这就是我发现的:

FragmentStateAdapter
具有方法
onBindViewHolder(holder,position,payloads)
,您可以覆盖该方法(不同于
onBindViewHolder(holder,position)
,后者是最终方法)。此方法在调用
notify
方法时调用,对于
notifyItemChanged()
notifyItemRangeChanged()
方法,您可以访问片段并手动更新它

下面是访问片段的代码:

public void onBindViewHolder(@NonNull FragmentViewHolder holder, int position, @NonNull List<Object> payloads) {

    String tag = "f" + holder.getItemId();

    Fragment fragment = fragmentManager.findFragmentByTag(tag);

    if (fragment != null) {
        //manual update fragment
    } else {
        // fragment might be null, if it`s call of notifyDatasetChanged() 
        // which is updates whole list, not specific fragment
        super.onBindViewHolder(holder, position, payloads);
    }
}
OldFragment.instance.containerId?.let {
    replace(it, NewFragment.instance)
}
public void onBindViewHolder(@NonNull FragmentViewHolder,int位置,@NonNull列表有效载荷){
String tag=“f”+holder.getItemId();
Fragment Fragment=fragmentManager.findFragmentByTag(标记);
if(片段!=null){
//手动更新片段
}否则{
//如果该片段调用notifyDatasetChanged(),则该片段可能为null
//这是更新整个列表,而不是特定的片段
super.onBindViewHolder(支架、位置、有效载荷);
}
}
FragmentStateAdapter
通过
“f”+holder.getItemId()
标记他的片段,这就是它工作的原因


您还可以使用
DiffUtil
,这是更好的方法。为了更好地理解
ViewPager2

,我今天遇到了同样的问题,我想在这里分享我的解决方案。我的解决方案不是截取
onBindViewHolder()
,而是简单地替换旧片段:

public void onBindViewHolder(@NonNull FragmentViewHolder holder, int position, @NonNull List<Object> payloads) {

    String tag = "f" + holder.getItemId();

    Fragment fragment = fragmentManager.findFragmentByTag(tag);

    if (fragment != null) {
        //manual update fragment
    } else {
        // fragment might be null, if it`s call of notifyDatasetChanged() 
        // which is updates whole list, not specific fragment
        super.onBindViewHolder(holder, position, payloads);
    }
}
OldFragment.instance.containerId?.let {
    replace(it, NewFragment.instance)
}
更具体地说:

我的应用程序有一个带有两个片段的ViewPager2。让我们把它们称为
MainFragment
DetailFragment
。在
MainFragment
中单击一个按钮,我想用
SecondMainFragment
替换它。反之亦然:点击
SecondMainFragment
中的按钮会将其自身替换为
MainFragment

class CustomFragmentStateAdapter(
    private val fragmentActivity: FragmentActivity
) : FragmentStateAdapter(fragmentActivity) {

    var displaySecondMainFragment: Boolean = false
        set(value) {
            if (field != value) {
                switchFragments(replaceWithSecondMainFragment = value)
            }
            field = value
        }

    private fun switchFragments(replaceWithSecondMainFragment: Boolean) {
        fragmentActivity.supportFragmentManager.commit {
            if (replaceWithSecondMainFragment) {
                MainFragment.instance.containerId?.let {
                    replace(it, SecondMainFragment.instance)
                }
            } else {
                SecondMainFragment.instance.containerId?.let {
                    replace(it, MainFragment.instance)
                }
            }
        }
    }
        
    // Standard adapter overrides
    override fun getItemCount(): Int = 2

    override fun createFragment(position: Int): Fragment = when (position) {
        0 -> if (displaySecondMainFragment) SecondMainFragment.instance else MainFragment.instance
        else -> DetailFragment.instance
    } 
}
使用此扩展属性获取片段所在容器的id:

inline val Fragment.containerId: Int?
    get() = (view?.parent as? ViewGroup?)?.id
最后,我只需要做这样的事情:

button.setOnClickListener {
    customFragmentStateAdapter.displaySecondMainFragment = true // Or false for the other way
}

我也试着这么做。你解决问题了吗?很高兴知道这个
onBindViewHolder(…)
方法存在,并且可以像这样重写它来更新
ViewPager2
中的片段。任何使用这种方法的人都必须考虑到,它依赖于
FragmentStateAdapter
的实现细节,即使用标记
“f”+holder.getItemId()
将片段添加到
FragmentManager
。您应该使用一些UI测试来覆盖您的应用程序,以便在开发时检测此实现细节是否在未来版本的
FragmentStateAdapter
类中发生过更改。是的,这是不安全的。也许如果手动比较碎片标记而不是使用
fragmentManager.findFragmentByTag(“f”+holder.getItemId())
将使其安全。在我的情况下,我有一个FragmentActivity,你知道我如何仍能使其工作吗?