我想了解这个异常,以便实现正确的修复。
有一个ViewPager,它使用FragmentStatePagerAdapter通过getItem和MyFragmentClass.newInstance(...)
实例化2个片段。
适配器的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;
}
因此,一个bundle被传递到那里,其中一些状态引用了我的旧片段,但这与当前状态(mActive
)不对应,并且抛出了异常。
我不明白这背后的想法是什么,也不明白我应该用什么方式实例化这些片段。
我尝试了一个从另一个上下文中得到的技巧:
pager.setOffscreenPageLimit(1);
为了避免碎片在屏幕外时被破坏(在2页的viewpager的情况下,尽管不知道它是否与状态适配器配合良好)。但似乎并没有关联,至少,它没有帮助,仍然得到同样的例外。
捕获异常会导致页面为空白。
这可能会有所帮助-
@Override
public Parcelable saveState() {
return null;
}
在FragmentStatePagerAdapter
中添加上面的行。
如果不希望片段在屏幕外被回收,则应该使用FragmentPagerAdapter
而不是FragmentStatePagerAdapter
。
问题详细信息
默认情况下,FragmentStatePagerAdapter将保存并恢复ViewPager的状态。在恢复时,如果碎片实例由于某种原因而被杀死,则FragmentManger将抛出此异常
解决方案:
要解决此问题,需要覆盖FragmentStatePagerAdapter中的restoreState方法,并放入try-catch块。它将防止崩溃,也将在正常情况下保留视图寻呼机的碎片状态。
@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在正常情况下不会保留其状态。
如果使用ViewPager2,则在ViewPager2对象上使用此方法
viewPager2.setSaveEnabled(false);
我在这个问题上挣扎了一整天,但现在我找到了解决方案。
private ViewPager _mViewPager;
_mViewPager.setOffscreenPageLimit(5);
//5 is how much page you have.
setOffscreenPageLimit
设置应保留在视图层次结构中处于空闲状态的当前页面两侧的页面数。超过此限制的页面将在需要时从适配器重新创建。
使用"活动生命周期"而不是"片段"。
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);
非常好!由于调用restoreState
时重复pager.setAdapter(adapter);
导致异常:
Fragment no longer exists for key f0: index 0
我们可以发布碎片根视图
public void onDestroyView() {
super.onDestroyView();
if (isRecyclerRootViewAlways()) {
mRootView = null;//<--
}
mMyFragmentLifecycle.onFragmentDestroyView(this);
}
viewpager2与recyclerview分离时出现问题https://issuetracker.google.com/issues/154751401
据我所知,有两种修复方法:
-
在xmlviewpager2组件中添加此行。
android:saveEnabled="false"
但是上面的一个没有保存当前实例,片段每次弹出时都会重新创建该实例。
-
建议的解决方案是在您的碎片上添加以下行
override fun onDestroyView() { super.onDestroyView() viewPager.adapter = null }
使用
getFragmentManager()
片段内适配器不是子片段管理器
PagerAdapter adapter = new PagerAdapter(getFragmentManager());
我希望这能有所帮助。