如何使用FragmentStateAdapter和ViewPager2阻止切换Tab时Fragment刷新
解决ViewPager2 + FragmentStateAdapter切换Tab时Fragment重建的问题
问题根源分析
你的代码存在两个核心问题,直接导致Fragment切换时重建:
getItemId实现错误:你用Fragment的hashCode作为Item ID,但Fragment的hashCode并非固定值,ViewPager2会通过对比Item ID判断是否需要重新创建Fragment,ID变化就会触发重建操作。- Adapter设置时机错误:先给ViewPager2绑定Adapter,再往Adapter里添加Fragment,导致Adapter初始化时数据为空,后续添加数据后触发不必要的视图重建。
修复步骤
1. 修正Adapter的getItemId方法
将getItemId改为返回固定的位置ID(如果Fragment列表不会动态增删),保证每个位置的ID唯一且不变:
public class LandingAdapter extends FragmentStateAdapter { private final List<Fragment> mFragmentList = new ArrayList<>(); public LandingAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) { super(fragmentManager, lifecycle); } @NonNull @Override public Fragment createFragment(int position) { return mFragmentList.get(position); } @Override public int getItemCount() { return mFragmentList.size(); } public void addFragment(Fragment fragment) { mFragmentList.add(fragment); } @Override public long getItemId(int position) { // 返回固定的位置ID,避免因Fragment实例变化导致ID改变 return position; } // 可选:如果需要支持动态修改Fragment列表,需重写该方法 @Override public boolean containsItem(long itemId) { return itemId >= 0 && itemId < mFragmentList.size(); } }
2. 调整Activity中Adapter的初始化顺序
先完成Fragment的添加,再将Adapter绑定到ViewPager2:
ViewPager2 mViewPager = findViewById(R.id.ViewPager); TabLayout mTabLayout = findViewById(R.id.TabLayout); LandingAdapter mAdapter = new LandingAdapter(getSupportFragmentManager(), getLifecycle()); int savedAppMode = SharedPrefs.getInt(this, "app_mode_checked_item",0); if (savedAppMode == 1) { mAdapter.addFragment(new ItemsFrag1()); mAdapter.addFragment(new ContactsFrag1()); mAdapter.addFragment(new ImagesFrag1()); } else { mAdapter.addFragment(new ItemsFrag2()); mAdapter.addFragment(new ContactsFrag2()); mAdapter.addFragment(new ImagesFrag2()); } // 先填充数据再绑定Adapter mViewPager.setAdapter(mAdapter); // 可选:设置缓存页面数,让所有Tab的Fragment都保持在内存中(根据需求调整) mViewPager.setOffscreenPageLimit(2); new TabLayoutMediator(mTabLayout, mViewPager, (tab, position) -> { switch (position) { case 0: tab.setText("Items"); tab.setIcon(R.drawable.x_icon_photo); break; case 1: tab.setText("Contacts"); tab.setIcon(R.drawable.x_icon_video); break; case 2: tab.setText("Images"); tab.setIcon(R.drawable.x_icon_download); break; } }).attach();
额外说明
setOffscreenPageLimit设置的是当前页面左右两侧需要缓存的页面数,设置为2时,三个Tab的Fragment都会被缓存,切换时不会重建。- 如果需要动态修改Fragment列表,不要直接修改
mFragmentList,需调用Adapter的notifyDataSetChanged(),同时保证getItemId返回的ID能唯一标识每个Fragment(比如给每个Fragment分配固定的唯一ID,而非依赖position)。
内容的提问来源于stack exchange,提问作者user13926345
相关产品推荐
相关产品推荐

