wake-up-neo.net

IllegalStateException: <MyFragment> befindet sich derzeit nicht im FragmentManager

Ich weiß, es klingt wie ein Duplikat von FragmentStatePagerAdapter IllegalStateException: <MyFragment> ist momentan nicht im FragmentManager Aber seine Lösung ist für meinen Fall nicht relevant.

Ich bekomme den folgenden Absturz sehr selten:

Java.lang.RuntimeException: Aktivität {MyActivity} kann nicht angehalten werden: 

...

Verursacht durch: Java.lang.IllegalStateException: Fragment MyFragment {40648258 id = 0x7f070051} ist derzeit nicht im FragmentManager um Android.support.v4.app.FragmentManagerImpl.putFragment (MT: 516) um Android.support.v4.app.FragmentStatePagerAdapter.saveState (MT: 185) um Android.support.v4.view.ViewPager.onSaveInstanceState (MT: 881) 

...

at Android.view.View.saveHierarchyState (View.Java:6238) um ​​ com.Android.internal.policy.impl.PhoneWindow.saveHierarchyState (PhoneWindow.Java:1522) bei Android.app.Activity.onSaveInstanceState (Activity.Java:1138) um ​​ Android.support.v4.app.FragmentActivity.onSaveInstanceState (MT: 480) um MyActivity.onSaveInstanceState (MT: 336)

Es scheint, dass dies der seltsame Code ist, den ich unter FragmentStatePagerAdapter nicht verstehen kann:

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

Es sieht so aus, als ob der Adapter meine Fragment von mFragments erhält, aber seinen Status nicht zu FragmentManager hinzufügen kann.

Ich habe keine Möglichkeit gefunden, dies auf meinen Testgeräten neu zu erstellen. Ich habe es nur von einigen Benutzern erhalten.

Ich verwende das Unterstützungspaket v4.

Irgendwelche Hilfe? Danke.

34
marmor

Die FragmentStatePagerAdapter ist ein schrecklicher Code, der mit Fehlern behaftet ist, die von Google bestätigt werden oder nicht.

    @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

Dies kann vorkommen, wenn ein FragmentStatePagerAdapter auf einem ViewPager festgelegt ist, die Fragmente jedoch aufgefüllt sind, aber aufgrund der vor dem Layout des ViewPagers angehaltenen Aktivität noch nicht zum FragmentManager hinzugefügt wurden. Dies wird zuverlässig durch das Starten einer anderen Aktivität von onCreate aus mit dem Adapter in onCreate ausgelöst. In dieser Situation ist es am besten, den Adapter nicht weiter einzustellen, sondern aufActivityResult zu setzen. Sehen Sie sich diese Ausgabe an: https://code.google.com/p/Android/issues/detail?id=77285

Ich habe auch einen Patch eingereicht, um zu überprüfen, ob das Fragment hinzugefügt wurde, bevor der Status gespeichert wird.

10
piusvelte

Ich bin auf die genaue Ausnahme gestoßen.

In meinem Fall habe ich mehrere Fragmente, die von FragmentPagerStateAdapter verwaltet werden, aber manchmal wird das Fragment in Position verschoben. 

Ich überschreibe getItemPosition () und gebe die neue Position zurück und rufe notifyDataSetChanged () auf, um ViewPager zu aktualisieren. Alles funktioniert gut, aber manchmal, wenn ich den ViewPager verlasse, kommt es zum Absturz.

Nachdem ich einige Stunden hineingegraben hatte, fand ich einen Fehler in FragmentPagerStateAdapter.

Der Adapter speichert nicht nur Zustände, sondern auch die Fragmente, die er als "aktiv" betrachtet.

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

Es wird jedoch nicht geprüft, ob die Position der Fragmente ungültig gemacht oder verschoben wurde.

So bekommt es diese Ausnahme:

1.Ich habe A, B, C drei Fragmente in ViewPager und Adapter.

2.Ich habe die Position im Adapter auf B, A, C geändert und notifyDataSetChanged () aufgerufen.

3.ViewPager ordnet das Fragment in einer neuen Reihenfolge neu an. Der mFragments-Cache ist jedoch immer noch {A, B, C}.

4.Wischen Sie ein paar Seiten, ViewPager fordert den Adapter auf, das erste Fragment zu zerstören. dann wird das Fragment B anstelle von A im Cache auf Null gesetzt. Jetzt ist das mFragments {null, A, C}.

5.Lassen Sie den ViewPager und onSaveInstanceState wird ausgelöst. Alle Fragmente in mFragments werden als aktiv betrachtet und putFragment () wird aufgerufen, bis FragmentManagerImpl A nicht darin gefunden hat und eine Exception auslöst.

Also hier ist meine nicht so sanfte Lösung:

1.Kopieren Sie den gesamten FragmentPagerStateAdapter-Quellcode.

2. Überschreiben Sie die notifyDataSetChanged () - Methode, um die Caches wie folgt neu anzuordnen.

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

Hoffe das funktioniert für einige von euch!

7
Joseph

Verwenden Sie getChildFragmentManager() anstelle von getFragmentManager(), wenn Sie eine Fragmenthierarchie haben. z.B. wenn Sie Pager Fragmente haben.

7
ViliusK

Verwenden Sie das Folgende 

final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(f, key);

anstatt 

mFragmentManager.putFragment(state, key, f);

und übergeben Sie das Bundle explizit ..

Als Referenz,

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

2
Sripathi

Wenn Ihr ViewPager in einem Fragment angeordnet ist (keine Aktivität):

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

2
Thomas G.

Schreiben Sie in Activity onCreate () - Methode:

pager = (ViewPager) findViewById(R.id.pager);
    adapter = new SwipePagerAdapter(getSupportFragmentManager());
    pageOneFragment = new PageOneFragment();
    adapter.addFragment(pageOneFragment);

Adaptercode:

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

Rufen Sie FragmentStatePagerAdapter.instantiateItem () mit Ihrem eigenen Code auf? Im Allgemeinen sollte der ViewPager der einzige Client sein, der diese Methode aufruft. Es kann jedoch leicht zu Problemumgehungen für andere Einschränkungen kommen, die auf dieser Methode beruhen.

Aufgrund der Funktionsweise von FragmentStatePagerAdapter würde ich vermuten, dass der Adapter das Fragment instanziiert hat, es zu seiner internen Sammlung von Fragmenten hinzugefügt hat und sogar eine Transaktion gestartet hat, um dieses Fragment dem FragmentManager hinzuzufügen. Die Fragmenttransaktion wurde jedoch nicht festgeschrieben oder ausgeführt . Genau dies tut instantiateItem(), wenn das angeforderte Fragment noch nicht instanziiert wurde.

Eine andere Methode, FragmentStatePagerAdapter.finishUpdate(), ist für das Festschreiben der Transaktion sowie für die Ausführung der ausstehenden Transaktionen verantwortlich. finishUpdate () muss nach instantiateItem () aufgerufen werden, andernfalls kann das Fragment nicht zum FragmentManager hinzugefügt werden.

Versuchen Sie etwas so:

// Get the current Fragment presented by the ViewPager.
pagerAdapter.startUpdate(viewPager);
Fragment fragment = pagerAdapter.instantiateItem(viewPager, viewPager.getCurrentItem());
pagerAdapter.finishUpdate(viewPager);
return fragment;

startUpdate () hat eine leere Implementierung ab API Level 19.

1
James Wald

Sie könnten versuchen, mFragmentManager.add(); zu verwenden.

0
superuser

Versuchen Sie es mit 

use fragmentTransaction.add()
0
Bedimindtricks

Prüfen Sie nochmals, ob Sie onSaveInstanceState und onRestoreInstanceState in Ihre Aktivität implementiert haben, und überprüfen Sie, ob sie den Status Ihrer Fragmente korrekt speichern und laden.

0
Hubert

Fragmente im ViewPager werden korrigiert, anstatt zu versuchen, die Fragmente im Adapter zu ersetzen, versuchen Sie, einen anderen Satz von Fragmenten zu ändern und notifyDataSet zu ändern, oder nutzen Sie FrameLayout, um ein anderes Fragment über das aktuelle Fragment des Registers der Ansichts-Pager-Registerkarte anzuzeigen.

Es gibt meine Lösung, die funktioniert:

Swipe-Geste, die auf Fragmentebene zusammen mit ViewPager angewendet wird, wobei der Standard-Swipe deaktiviert ist

0
Abhinav Saxena