You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:31:25