IllegalStateException:<myfragment>当前不在FragmentManager </myfragment>中

时间:2012-08-13 15:37:19

标签: java android android-fragments android-viewpager illegalstateexception

我知道这听起来像是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。

有任何帮助吗? 感谢。

12 个答案:

答案 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);

并明确传递包。

供参考,

http://developer.android.com/reference/android/app/FragmentTransaction.html#add%28android.app.Fragment,%20java.lang.String%29

答案 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)

仔细检查您是否在活动中实施了onSaveInstanceStateonRestoreInstanceState,并验证他们是否正确保存并加载了片段的状态。