我知道这听起来像是FragmentStatePagerAdapter IllegalStateException: <MyFragment> is not currently in the FragmentManager的副本 但他的解决方案与我的案例无关。
我很少遇到以下崩溃:
java.lang.RuntimeException:无法暂停活动{MyActivity}:
...
引起:java.lang.IllegalStateException:Fragment MyFragment {40648258 id = 0x7f070051}目前不在FragmentManager中 android.support.v4.app.FragmentManagerImpl.putFragment(MT:516)at android.support.v4.app.FragmentStatePagerAdapter.saveState(MT:185)at android.support.v4.view.ViewPager.onSaveInstanceState(MT:881)
...
在android.view.View.saveHierarchyState(View.java:6238)处 com.android.internal.policy.impl.PhoneWindow.saveHierarchyState(PhoneWindow.java:1522) 在android.app.Activity.onSaveInstanceState(Activity.java:1138)at android.support.v4.app.FragmentActivity.onSaveInstanceState(MT:480)at MyActivity.onSaveInstanceState(MT:336)
这似乎是FragmentStatePagerAdapter
无法理解的奇怪代码:
for (int i=0; i<mFragments.size(); i++) {
Fragment f = mFragments.get(i);
if (f != null) {
if (state == null) {
state = new Bundle();
}
String key = "f" + i;
mFragmentManager.putFragment(state, key, f);
}
}
看起来适配器从Fragment
获取了mFragments
,但无法将其状态添加到FragmentManager
。
我找不到任何方法在我的测试设备上重新创建它,只是从一些用户那里收到了这个。
我正在使用支持包v4。
有任何帮助吗? 感谢。
答案 0 :(得分:16)
FragmentStatePagerAdapter
是一段可怕的代码,充斥着谷歌承认与否的错误,因此我使用此代码来修复此特定崩溃:
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// Yet another bug in FragmentStatePagerAdapter that destroyItem is called on fragment that hasnt been added. Need to catch
try {
super.destroyItem(container, position, object);
} catch (IllegalStateException ex) {
ex.printStackTrace();
}
}
答案 1 :(得分:10)
当在ViewPager上设置FragmentStatePagerAdapter时会发生这种情况,其中Fragments正在填充,但由于在ViewPager发生布局之前暂停了Activity,因此尚未添加到FragmentManager。通过从onCreate启动另一个Activity可以实现这一点,同时适配器也设置在onCreate中。在这种情况下,最好不要设置适配器,而是将其设置为onActivityResult。请参阅此问题:https://code.google.com/p/android/issues/detail?id=77285
我还提交了一个补丁来检查在尝试保存状态之前是否添加了Fragment。
答案 2 :(得分:7)
如果您有片段层次结构,请使用getChildFragmentManager()
代替getFragmentManager()
。例如。如果你有片段寻呼机。
答案 3 :(得分:7)
我遇到了确切的例外。
就我而言,我有几个片段由FragmentPagerStateAdapter管理,但有时片段会切换到位。
我重写getItemPosition()并返回新位置并调用notifyDataSetChanged()来刷新ViewPager。一切正常,但有时当我离开ViewPager时会发生崩溃。
经过几个小时的挖掘,我发现FragmentPagerStateAdapter中存在一个错误。
适配器不仅缓存状态,还缓存它认为活跃的片段&#39;
private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
但它没有检查片段是否&#39;位置已失效或移动。
它是如何得到这个例外的:
1.我在ViewPager和Adapter中有A,B,C三个片段。
2.我将位置切换到适配器中的B,A,C并调用notifyDataSetChanged()。
3.ViewPager按新订单重新布局片段。但是mFragments缓存仍然是{A,B,C}
4.Swipe几页,ViewPager将要求适配器销毁第一个片段。 然后在缓存中将片段B(而不是A)设置为null。现在mFragments为{null,A,C}。
5.离开ViewPager,并触发onSaveInstanceState。 mFragments中的所有片段都被认为是活动的,并且调用putFragment()直到FragmentManagerImpl发现A不在其中并抛出异常。
所以,这是我不太温和的解决方案:
1.复制整个FragmentPagerStateAdapter源代码。
2.Override notifyDataSetChanged()方法重新排列缓存,如下所示。
@Override
public void notifyDataSetChanged() {
List<Fragment> oldFragments = new ArrayList<>(mFragments);
List<Fragment.SavedState> oldStates = new ArrayList<>(mSavedState);
for (int i = 0; i < getCount(); i++) {
if (i < mFragments.size()) {
Fragment f = mFragments.get(i);
if (f != null) {
int newPosition = getItemPosition(f);
if (newPosition == POSITION_UNCHANGED || newPosition == i) {
} else if (newPosition == POSITION_NONE) {
if (i < mSavedState.size()) {
mSavedState.set(i, null);
}
mFragments.set(i, null);
} else {
while (i >= mFragments.size()) {
mFragments.add(null);
}
if (oldStates.size() > i) {
mSavedState.set(newPosition, oldStates.get(i));
}
mFragments.set(newPosition, oldFragments.get(i));
}
} else {
/*
* No Fragment but that's possible there's savedState and position has
* changed.
*/
}
}
}
super.notifyDataSetChanged();
}
希望对你们中的一些人有用!
答案 4 :(得分:2)
使用以下
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(f, key);
而不是
mFragmentManager.putFragment(state, key, f);
并明确传递包。
供参考,
答案 5 :(得分:2)
如果您的ViewPager是片段(不是活动)内的布局:
mViewPager.setAdapter(new MyFragmentStatePagerAdapter(getChildFragmentManager()));
答案 6 :(得分:1)
写入Activity onCreate()方法:
pager = (ViewPager) findViewById(R.id.pager);
adapter = new SwipePagerAdapter(getSupportFragmentManager());
pageOneFragment = new PageOneFragment();
adapter.addFragment(pageOneFragment);
适配器代码:
public class SwipePagerAdapter extends FragmentStatePagerAdapter
{
private final ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
public SwipePagerAdapter(FragmentManager fm)
{
super(fm);
}
@Override
public Fragment getItem(int position)
{
return mFragments.get(position);
}
@Override
public int getCount()
{
return mFragments.size();
}
public void addFragment(Fragment fragment)
{
mFragments.add(fragment);
notifyDataSetChanged();
}
@Override
public void destroyItem(ViewGroup container, int position, Object object)
{
super.destroyItem(container, position, object);
}
@Override
public CharSequence getPageTitle(int position)
{
return super.getPageTitle(position);
}}
答案 7 :(得分:1)
您是否从自己的代码中调用 FragmentStatePagerAdapter.instantiateItem()?通常,ViewPager应该是调用此方法的唯一客户端,但是,对于依赖此方法的其他限制,很容易遇到变通方法。
由于道路FragmentStatePagerAdapter
作品,我猜想,适配器实例化的片段,其添加到它的片段的内部集合,甚至开始交易该片段添加到FragmentManager,但片段事务尚未提交或执行。这正是instantiateItem()
在尚未实例化请求的片段时的作用。
另一种方法FragmentStatePagerAdapter.finishUpdate()
负责提交事务以及执行挂起的事务。的 finishUpdate()必须instantiateItem()或其他的片段可以不被添加到FragmentManager。之后调用
尝试这样的事情:
// Get the current Fragment presented by the ViewPager.
pagerAdapter.startUpdate(viewPager);
Fragment fragment = pagerAdapter.instantiateItem(viewPager, viewPager.getCurrentItem());
pagerAdapter.finishUpdate(viewPager);
return fragment;
从API级别19开始,startUpdate()有一个空实现。
答案 8 :(得分:0)
您可以尝试使用mFragmentManager.add();
答案 9 :(得分:0)
尝试使用
use fragmentTransaction.add()
答案 10 :(得分:0)
ViewPager中的片段是固定的,而不是尝试替换适配器中的片段,尝试给出一组不同的片段并更改notifyDataSet,或者利用FrameLayout在视图分页器选项卡的当前片段上显示另一个片段
我的解决方案有效:
Swipe Gesture applied at Fragment level along with ViewPager with it's default swipe disabled
答案 11 :(得分:0)
仔细检查您是否在活动中实施了onSaveInstanceState
和onRestoreInstanceState
,并验证他们是否正确保存并加载了片段的状态。