Подтвердить что ты не робот

IllegalStateException: <MyFragment> в настоящее время отсутствует в FragmentManager

Я знаю, это звучит как дубликат FragmentStatePagerAdapter IllegalStateException: <MyFragment> в настоящее время не находится в FragmentManager но его решение не имеет отношения к моему делу.

Я получаю следующий сбой очень редко:

java.lang.RuntimeException: невозможно приостановить действие {MyActivity}:

...

Вызвано: java.lang.IllegalStateException: фрагмент MyFragment {40648258 id = 0x7f070051} в настоящее время не находится в FragmentManager at android.support.v4.app.FragmentManagerImpl.putFragment(MT: 516) at android.support.v4.app.FragmentStatePagerAdapter.saveState(MT: 185) в 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) в android.support.v4.app.FragmentActivity.onSaveInstanceState(MT: 480) в MyActivity.onSaveInstanceState(МТ: 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.

Любая помощь? Спасибо.

4b9b3361

Ответ 1

FragmentStatePagerAdapter - это ужасный фрагмент кода, пронизанный ошибками, признанными Google Google, и поэтому я использую этот код для исправления этого конкретного сбоя:

    @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();
        }
    }

Ответ 2

Это может произойти, если FragmentStatePagerAdapter установлен на ViewPager, с заполненными фрагментами, но еще не добавлен в FragmentManager из-за приостановки активности, прежде чем макет произошел для ViewPager. Это надежно ударит, запустив другое действие из onCreate, а адаптер также установлен в onCreate. В этой ситуации лучше всего отложить настройку адаптера и вместо этого установить его onActivityResult. См. Эту проблему: https://code.google.com/p/android/issues/detail?id=77285

Я также представил патч для проверки того, что Фрагмент был добавлен, прежде чем пытаться сохранить состояние.

Ответ 3

Используйте getChildFragmentManager() вместо getFragmentManager(), если у вас есть иерархия фрагментов. например. если у вас есть фрагмент пейджера.

Ответ 4

Я столкнулся с точным исключением.

В моем случае у меня есть несколько фрагментов, которыми управляет FragmentPagerStateAdapter, но иногда фрагмент будет включен в положение.

Я переопределяю getItemPosition() и верну новую позицию и вызываю notifyDataSetChanged(), чтобы обновить ViewPager. Все работает нормально, но иногда, когда я покидаю ViewPager, происходит сбой.

После нескольких часов работы в нем я обнаружил ошибку в FragmentPagerStateAdapter.

Адаптер не только кэширует состояния, но также кэширует фрагменты, которые он считает активными

private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();

Но он не проверяет, было ли положение фрагментов отменено или перемещено.

Как получить это исключение:

1. У меня есть A, B, C три фрагмента в ViewPager и Adapter.

2.I переключил положение на B, A, C в адаптере и вызвал notifyDataSetChanged().

3.ViewPager переформатирует фрагмент по новому порядку. Но кеш mFragments все еще {A, B, C}

4.Swipe несколько страниц, ViewPager попросит адаптер уничтожить первый фрагмент. , тогда фрагмент B, а не A, устанавливается в нуль в кеше. Теперь mFragments является {null, A, C}.

5. Запустите ViewPager и активируется onSaveInstanceState. все фрагменты в mFragments считаются активными и putFragment() вызывается до тех пор, пока FragmentManagerImpl не найдет A в нем и не выбросит Exception.

Итак, вот мое не очень мягкое решение:

1. Содержит весь исходный код FragmentPagerStateAdapter.

2. Переустановите метод 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 possible there savedState and position has
         * changed.
         */
      }
    }
  }
  super.notifyDataSetChanged();
}

Надеюсь, что это сработает для вас!

Ответ 6

Если ваш ViewPager размещен внутри фрагмента (а не активности):

mViewPager.setAdapter(new MyFragmentStatePagerAdapter(getChildFragmentManager()));

Ответ 7

Записать в действие метод 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);
}}

Ответ 8

Вы вызываете 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;

startUpdate() имеет пустую реализацию с уровня API 19.

Ответ 9

Вы можете попробовать использовать mFragmentManager.add();

Ответ 10

Попробуйте использовать

use fragmentTransaction.add()

Ответ 11

Фрагменты в ViewPager исправлены, вместо того, чтобы пытаться заменить фрагменты в адаптере, попытайтесь дать другой набор фрагментов и notifyDataSet измениться или воспользоваться преимуществом FrameLayout, чтобы показать другой фрагмент над вкладкой пейджера представления текущего фрагмента.

Есть мое решение, которое работает:

Пролистайте жестом, применяемый на уровне фрагмента вместе с ViewPager с отключенным салфеткой по умолчанию

Ответ 12

Дважды проверьте, были ли вы реализованы onSaveInstanceState и onRestoreInstanceState в своей деятельности и убедитесь, что они правильно сохраняют и загружают состояние ваших фрагментов.