61

我想了解此异常以实施适当的修复。

有一个 ViewPager,它使用 FragmentStatePagerAdapter 通过 getItem 和MyFragmentClass.newInstance(...).

适配器的 getItem 如下所示:

@Override
public Fragment getItem(int position) {
    Fragment fragment = null;

    switch(position) {
        case 0:
            fragment = MyFragment2.newInstance(par1);
            break;
        case 1:
            fragment = MyFragment2.newInstance(par2, par3);
            break;
    }
    return fragment;
}

问题:

当活动被销毁并再次创建时,适配器再次被实例化,片段再次使用MyFragmentClass.newInstance(...)...创建,但随后在这一行:

pager.setAdapter(adapter);

我得到了提到的例外。

我查看了引发异常的源代码,是这样的:

@Override
public Fragment getFragment(Bundle bundle, String key) {
    int index = bundle.getInt(key, -1);
    if (index == -1) {
        return null;
    }
    if (index >= mActive.size()) {
        throw new IllegalStateException("Fragement no longer exists for key "
                + key + ": index " + index);
    }
    Fragment f = mActive.get(index);
    if (f == null) {
        throw new IllegalStateException("Fragement no longer exists for key "
                + key + ": index " + index);
    }
    return f;
}

因此,在那里传递了一个包,其中包含一些引用我的旧片段的状态,但这与当前状态 ( ) 不对应mActive,并且引发了异常。

我不明白这背后的想法是什么,或者我应该以哪种方式实例化片段。

我尝试了一个从另一个上下文中得到的技巧:

pager.setOffscreenPageLimit(1);

为了避免片段在屏幕外时被破坏(在2页视图页面的情况下,虽然不知道它是否适用于状态适配器)。但似乎没有关系,至少,它没有帮助,仍然得到同样的异常。

捕获异常会导致页面为空白。

4

8 回答 8

70

这可以帮助 -

@Override
public Parcelable saveState() {
    return null;
}

将上面的行添加到FragmentStatePagerAdapter.

于 2015-01-22T14:39:09.823 回答
27

如果您不希望碎片在屏幕外时被回收,您应该使用FragmentPagerAdapter而不是FragmentStatePagerAdapter.

于 2012-09-04T20:07:41.103 回答
20

问题详情

默认情况下,FragmentStatePagerAdapter 将保存和恢复 ViewPager 的状态。在恢复时,如果片段实例由于某种原因被杀死,那么 FragmentManger 将抛出此异常。

解决方案:

要解决这个问题,需要重写我们的 FragmentStatePagerAdapter 中的 restoreState 方法并放入 try catch 块。它将防止崩溃,并且它将在正常情况下保留 viewpager 的片段状态。

@Override
public void restoreState(Parcelable state, ClassLoader loader) {
    try {
        super.restoreState(state, loader);
    } catch (Exception e) {
        Log.e("TAG", "Error Restore State of Fragment : " + e.getMessage(), e);
    }
}

注意:我们可以使用 FragmentPagerAdapter 或 Override saveState () 并返回 null 也可以解决此问题,但 viewpager 在正常情况下不会保留其状态。

于 2018-12-19T10:14:57.883 回答
12

如果您使用的是 ViewPager2 则在 ViewPager2 对象上使用此方法

viewPager2.setSaveEnabled(false);
于 2020-01-18T19:27:08.643 回答
4

我整天都在努力解决这个问题,但现在我找到了解决方案。

private ViewPager _mViewPager;
_mViewPager.setOffscreenPageLimit(5);
//5 is how much page you have.

setOffscreenPageLimit设置应保留到处于空闲状态的视图层次结构中当前页面的任一侧的页面数。超出此限制的页面将在需要时从适配器重新创建。

于 2016-03-19T18:02:45.093 回答
2

使用活动生命周期而不是片段。

public class MyAdapter extends FragmentStateAdapter {

    public MyAdapter (@NonNull Fragment fragment) {
        super(fragment.getFragmentManager(), fragment.getActivity().getLifecycle());
    }
}

或者正如其他人提到的那样禁用ViewPager2保存状态。

  • 在布局中:android:saveEnabled="false"
  • 在代码中:
    • viewPager.setSaveEnabled(false);
    • viewPager.setSaveFromParentEnabled(false);
于 2020-10-28T18:41:10.683 回答
1

很好 !pager.setAdapter(adapter);由于调用时重复restoreState导致异常:

Fragment no longer exists for key f0: index 0

我们可以释放 Fragment RootView

    public void onDestroyView() {
    super.onDestroyView();
    if (isRecyclerRootViewAlways()) {
        mRootView = null;//<--
    }
    mMyFragmentLifecycle.onFragmentDestroyView(this);
}
于 2017-05-04T02:47:04.990 回答
-4

利用

获取片段管理器()

在片段适配器而不是子片段管理器中

PagerAdapter adapter = new PagerAdapter(getFragmentManager());

我希望这有帮助。

于 2018-01-20T06:45:25.133 回答