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.
Любая помощь?
Спасибо.
Ответы
Ответ 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();
}
Надеюсь, что это сработает для вас!
Ответ 5
Используйте следующие
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
Ответ 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
в своей деятельности и убедитесь, что они правильно сохраняют и загружают состояние ваших фрагментов.