it-swarm.com.ru

IllegalStateException: <MyFragment> в данный момент отсутствует в FragmentManager

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

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

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

...

Вызывается: Java.lang.IllegalStateException: Fragment MyFragment {40648258 id = 0x7f070051} в данный момент отсутствует в FragmentManager в Android.support.v4.app.FragmentManagerImpl.putFragment (MT: 516) в 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 (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.

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

34
marmor

FragmentStatePagerAdapter - это ужасный кусок кода, изобилующий ошибками, которые 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();
        }
    }
16
Greg Ennis

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

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

10
piusvelte

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

В моем случае у меня есть несколько фрагментов, управляемых 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 и адаптере.

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

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

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

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

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

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's possible there's savedState and position has
         * changed.
         */
      }
    }
  }
  super.notifyDataSetChanged();
}

Надеюсь, что это работает для некоторых из вас!

7
Joseph

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

7
ViliusK

Используйте следующее 

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

2
Sripathi

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

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

2
Thomas G.

Напишите в методе 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);
}}
1
Abdul Rehman

Вы звоните FragmentStatePagerAdapter.instantiateItem () из своего собственного кода? Как правило, ViewPager должен быть единственным клиентом, который вызывает этот метод, однако легко найти обходные пути для других ограничений, которые зависят от этого метода.

Из-за способа работы FragmentStatePagerAdapter я бы предположил, что адаптер создал экземпляр Fragment, добавил его во внутреннюю коллекцию Fragments и даже начал транзакцию для добавления этого Fragment в 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.

1
James Wald

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

0
superuser

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

use fragmentTransaction.add()
0
Bedimindtricks

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

0
Hubert

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

Вот мое решение, которое работает:

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

0
Abhinav Saxena