NotifyDataSetChanged 在带有 Viewpager2 的 FragmentStateAdapter 中不起作用
Posted
技术标签:
【中文标题】NotifyDataSetChanged 在带有 Viewpager2 的 FragmentStateAdapter 中不起作用【英文标题】:NotifyDataSetChanged not working in FragmentStateAdapter with Viewpager2 【发布时间】:2020-11-02 22:41:51 【问题描述】:我正在使用带有 FragmentStateAdapter 的 viewpager2 创建一个文件选择页面。在其中一个页面中,我显示了所有已安装的存储设备,我想在这些设备上点击以查看内容,但我希望内容位于另一个片段上。
我想在 viewpager 的最后一页中用另一个片段替换片段。但是使用下面的代码,页面只是刷新并且没有创建新的 Fragment,因为 createFragment
没有被调用。
我的 PagerAdapter
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;
这是我想替换为另一个 Fragment 的 Fragment 的构造函数:
private static FileSelection.PagerAdapter.FirstPageListener pageFragmentListener;
public FilesandFolder_Others_MainPage()
public FilesandFolder_Others_MainPage(FileSelection.PagerAdapter.FirstPageListener firstPageFragmentListener)
pageFragmentListener = firstPageFragmentListener;
点击视图, 应该调用以下代码:-
Bundle bundle = new Bundle();
bundle.putString("PATH", volumes.get(position).path);
pageFragmentListener.onSwitchToNextFragment(bundle);
我已经在 *** 上进行了搜索,但从来没有看到使用 viewpager 2 和 FragmentStateAdapter 的正确答案,因为每个人似乎都在使用 Viewpager
我试图实现这个:- FragmentStateAdapter for ViewPager2 notifyItemChanged not working as expected
但不明白如何编辑我的代码。请帮忙 提前致谢
【问题讨论】:
我也在尝试这样做。你解决问题了吗? 【参考方案1】:遇到了同样的问题,这就是我发现的:
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);
FragmentStateAdapter
的片段被"f" + holder.getItemId()
标记,这就是它起作用的原因。
您也可以使用DiffUtil
,这是更好的方法。解决这个问题的Here is good example,还有一些链接到底是为了更好的理解ViewPager2
。
【讨论】:
很高兴知道这个onBindViewHolder(...)
方法存在并且可以像这样被覆盖以更新ViewPager2
中的片段。任何使用这种方法的人都必须考虑到它依赖于FragmentStateAdapter
的实现细节,即片段被添加到带有标签"f" + holder.getItemId()
的FragmentManager
的事实。您应该使用一些 UI 测试覆盖您的应用程序,以便在开发时检测此实现细节是否在 FragmentStateAdapter
类的未来版本中发生更改。
是的,这不安全。也许如果手动比较片段标签而不是使用fragmentManager.findFragmentByTag("f" + holder.getItemId())
将是安全的。
在我的情况下,我有一个 FragmentActivity,你知道我怎么还能让它工作吗?【参考方案2】:
我今天遇到了同样的问题,并想在这里分享我的解决方案。我的解决方案不是拦截onBindViewHolder()
,而是简单地替换旧片段:
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
【讨论】:
【参考方案3】:你可以覆盖这两个方法
override fun getItemId(position: Int): Long
// generate new id
return getItem(position).hashCode().toLong()
override fun containsItem(itemId: Long): Boolean
// false if item is changed
return dataList.find it.hashCode().toLong() == itemId != null
【讨论】:
以上是关于NotifyDataSetChanged 在带有 Viewpager2 的 FragmentStateAdapter 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章
调用 notifyDataSetChanged 时未更新带有 customAdapter 的 ListView?
调用 notifydatasetchanged 后 Recyclerview 滚动到顶部
notifyDataSetChanged 不适用于 RecyclerView