ViewPager+Fragment横竖屏切换后崩溃:显示错误Fragment并触发异常
解决ViewPager横竖屏切换后点击Fragment崩溃的IndexOutOfBoundsException问题
这种问题我之前踩过坑!横竖屏切换后页面显示看起来正常,但一点击就崩索引越界,核心原因大概率是Fragment实例、Adapter数据源、ViewPager选中状态三者不同步——表面上显示的是你之前选中的页面,但实际绑定的Fragment可能是旧实例,它拿着的还是切换前的位置索引,而新的Adapter数据源已经发生了数量/顺序变化,导致索引失效。
下面给你几个针对性的解决方案,按优先级尝试:
1. 确保Adapter数据源和ViewPager选中状态正确恢复
横竖屏切换时系统会销毁重建Activity,你必须把Adapter的数据源和ViewPager的当前页面位置保存下来,重建时完全恢复:
// 在Activity的onSaveInstanceState中保存状态 @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // 保存数据源(确保数据模型可序列化/Parcelable) outState.putParcelableArrayList("fragment_data_list", yourDataList); // 保存当前选中的页面位置 outState.putInt("current_viewpager_page", viewPager.getCurrentItem()); } // 在Activity的onCreate中恢复状态 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState != null) { // 恢复数据源 yourDataList = savedInstanceState.getParcelableArrayList("fragment_data_list"); // 重新初始化Adapter yourAdapter = new StableArrayAdapter(getSupportFragmentManager(), yourDataList); viewPager.setAdapter(yourAdapter); // 恢复之前选中的页面 int currentPage = savedInstanceState.getInt("current_viewpager_page", 0); viewPager.setCurrentItem(currentPage); } else { // 首次加载的逻辑 loadDataAndInitViewPager(); } }
2. 修复StableArrayAdapter的稳定ID实现
既然用了StableArrayAdapter,核心就是要让getItemId返回与位置无关的唯一标识,而不是直接返回position。如果你的Adapter之前只是返回position,横竖屏切换后数据源稍有变动,系统就无法正确匹配旧的Fragment实例:
public class StableArrayAdapter extends FragmentPagerAdapter { private List<YourDataModel> dataList; // 构造方法省略... @Override public long getItemId(int position) { // 用数据模型的唯一ID作为ItemId,比如数据库主键、服务端返回的id return dataList.get(position).getUniqueId(); } @Override public boolean hasStableIds() { // 必须返回true,告诉系统这个Adapter的ItemId是稳定的 return true; } }
3. 让Fragment摆脱对位置索引的依赖
很多时候崩溃是因为Fragment在创建时保存了position,横竖屏切换后这个索引对应的数据源已经失效了。改成用数据唯一标识来获取数据:
// 创建Fragment时,传递数据ID而不是position public static YourFragment newInstance(long dataId) { YourFragment fragment = new YourFragment(); Bundle args = new Bundle(); args.putLong("DATA_ID", dataId); fragment.setArguments(args); return fragment; } // 在Fragment内部,通过ID获取数据而不是position @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); long dataId = getArguments().getLong("DATA_ID"); // 从全局数据源/ViewModel中根据ID查找对应数据 YourDataModel data = DataManager.getInstance().findDataById(dataId); // 用这个data初始化UI,彻底告别position依赖 }
4. 清除旧Fragment实例(极端情况)
如果前面的方法都没用,可以尝试在重建时清除ViewPager关联的旧Fragment实例,避免残留:
if (savedInstanceState != null) { // 清除所有旧的Fragment栈 getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); // 重新初始化Adapter和ViewPager yourAdapter = new StableArrayAdapter(getSupportFragmentManager(), yourDataList); viewPager.setAdapter(yourAdapter); viewPager.setCurrentItem(savedInstanceState.getInt("current_viewpager_page", 0)); }
本质上,这个问题就是横竖屏重建后,Fragment、数据源、ViewPager三者的关联关系被破坏了。只要用稳定的唯一标识替代位置索引,同时保证状态的完整恢复,就能彻底解决这个崩溃问题。
内容的提问来源于stack exchange,提问作者NullPointerException
相关产品推荐
相关产品推荐

