IllegalStateException:<MyFragment>当前不在FragmentManager中
我知道这听起来像是FragmentStatePagerAdapter IllegalStateException的重复:<MyFragment>目前不在FragmentManager中,但他的解决scheme与我的情况无关。
我很less遇到以下崩溃:
java.lang.RuntimeException:无法暂停活动{MyActivity}:
…
由于:java.lang.IllegalStateException:Fragment MyFragment {40648258 id = 0x7f070051}目前不在android.support.v4.app.FragmentManagerImpl.putFragment(MT:516)的FragmentManager中android.support.v4.app.FragmentStatePagerAdapter .saveState(MT:185)在android.support.v4.view.ViewPager.onSaveInstanceState(MT:881)
…
在android.app.Activity.onSaveInstanceState(Activity.java:1138)com.android.internal.policy.impl.PhoneWindow.saveHierarchyState(PhoneWindow.java:1522)android.view.View.saveHierarchyState(View.java:6238) )在MyActivity.onSaveInstanceState(MT:336)处的android.support.v4.app.FragmentActivity.onSaveInstanceState(MT:480)
这似乎是我从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); } }
它看起来像适配器从mFragments
获取我的Fragment
,但不能将其状态添加到FragmentManager
。
我找不到在我的testing设备上重新创build的任何方法,只有从一些用户那里得到这个。
我正在使用支持包v4。
任何帮助? 谢谢。
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(); } }
当在ViewPager上设置FragmentStatePagerAdapter时,可能会发生这种情况,由于在ViewPager发生布局之前活动暂停,碎片被填充,但尚未添加到FragmentManager。 这可以通过从onCreate启动另一个Activity来进行可靠的命中,同时Adapter也在onCreate中设置。 在这种情况下,最好暂缓设置Adapter,并将其设置为ActivityResult。 看到这个问题: https : //code.google.com/p/android/issues/detail?id = 77285
我还提交了一个补丁来检查在试图保存状态之前添加的碎片。
如果您有片段层次结构,请使用getChildFragmentManager()
而不是getFragmentManager()
。 例如。 如果你有片段寻呼机。
我遇到了确切的例外。
在我的情况下,我有几个片段由FragmentPagerStateAdapterpipe理,但有时片段将被切换到位置。
我重写getItemPosition()并返回新的位置,并调用notifyDataSetChanged()刷新ViewPager。 一切正常,但有时当我离开ViewPager发生崩溃。
经过几个小时的挖掘,我发现FragmentPagerStateAdapter中有一个bug。
适配器不仅可以caching状态,还可以caching它认为“活动”的碎片
private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>(); private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
但是它不检查碎片的位置是否失效或移动。
这就是它如何得到这个例外:
我在ViewPager和Adapter中有A,B,C三个片段。
2.我在适配器中将位置切换到B,A,C,并调用notifyDataSetChanged()。
3.ViewPager按新的顺序重新排列片段。 但是mFragmentscaching仍然是{A,B,C}
4.Swipe几页,ViewPager会要求适配器销毁第一个片段。 那么片段B,而不是A,在caching中设置为空。 现在mFragments是{null,A,C}。
5.保留ViewPager,并触发onSaveInstanceState。 mFragments中的所有片段都被认为是活动的,putFragment()被调用,直到FragmentManagerImpl发现A不在其中,并抛出一个Exception。
所以这里是我不那么温和的解决scheme:
1.复制整个FragmentPagerStateAdapter源代码。
2.覆盖notifyDataSetChanged()方法重新排列caching,如下所示。
@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(); }
希望对你们中的一些人有用!
如果你的ViewPager是在一个片段(而不是一个活动)内:
mViewPager.setAdapter(new MyFragmentStatePagerAdapter(get Child FragmentManager()));
使用以下内容
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.add(f, key);
代替
mFragmentManager.putFragment(state, key, f);
并明确传递包
以供参考,
你是从自己的代码调用FragmentStatePagerAdapter.instantiateItem()吗? 一般来说,ViewPager应该是唯一调用这个方法的客户端,但是,很容易find依赖于这个方法的其他限制的解决方法。
由于FragmentStatePagerAdapter
工作原理,我猜想适配器已经实例化了Fragment,将它添加到了Fragment的内部集合中,甚至开始了一个事务来将Fragment添加到FragmentManager中,但是这个碎片事务没有被提交或执行。 这正是instantiateItem()
在尚未实例化请求的Fragment时所做的。
另一种方法, FragmentStatePagerAdapter.finishUpdate()
负责提交事务以及执行未决事务。 finishUpdate()必须在instantiateItem()之后调用,否则Fragment可能不会被添加到FragmentManager中。
尝试这样的事情:
// Get the current Fragment presented by the ViewPager. pagerAdapter.startUpdate(viewPager); Fragment fragment = pagerAdapter.instantiateItem(viewPager, viewPager.getCurrentItem()); pagerAdapter.finishUpdate(viewPager); return fragment;
startUpdate()在API级别19中有一个空实现。
你可以尝试使用mFragmentManager.add();
在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 mFragments = new ArrayList();
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); }
}
尝试使用
use fragmentTransaction.add()
ViewPager中的片段是固定的,而不是尝试replace适配器中的片段,尝试给出不同的片段集合并更改notifyDataSet,或者利用FrameLayout在视图分页器选项卡的当前片段上显示另一个片段。
有我的解决scheme工作:
滑动手势与ViewPager一起应用于片段级别,禁用默认滑动
仔细检查您的activity是否已经实现了onSaveInstanceState
和onRestoreInstanceState
,并validation它们是否正确保存并加载了碎片的状态。