Ist dies der richtige Weg, um den Fragment-Back-Stack zu bereinigen, wenn Sie einen tief verschachtelten Stack verlassen?

130

Ich verwende die Android-Kompatibilitätsbibliothek, um Fragmente zu implementieren, und habe das Layoutbeispiel so erweitert, dass ein Fragment eine Schaltfläche enthält, die ein anderes Fragment auslöst.

Im Auswahlbereich links habe ich 5 auswählbare Elemente - A B C D E.

Jeder lädt ein Fragment (via FragmentTransaction:replace) in das Detailfenster -a b c d e

Jetzt habe ich das Fragment eum eine Schaltfläche erweitert , die ein weiteres Fragment e1auch im Detailbereich lädt . Ich habe dies mit der eonClick-Methode von fragment wie folgt gemacht:

FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.replace(R.id.details_frag, newFrag);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();

Wenn ich folgende Auswahl treffe:

E - e - e1 - D - E

Dann ebefindet sich das Fragment im Detailbereich. Das ist in Ordnung und was ich will. Wenn ich jedoch backan dieser Stelle auf den Knopf drücke, geschieht nichts. Ich muss zweimal darauf klicken, weil e1es noch auf dem Stapel ist. Außerdem habe ich nach dem Klicken eine Nullzeiger-Ausnahme in onCreateView erhalten:

Um dieses Problem zu lösen, habe ich bei jeder A B C D EAuswahl Folgendes hinzugefügt :

FragmentManager fm = getActivity().getSupportFragmentManager();
for(int i = 0; i < fm.getBackStackEntryCount(); ++i) {
    fm.popBackStack();
}

Sie fragen sich nur, ob dies die richtige Lösung ist oder ob ich etwas anderes tun sollte?

PJL
quelle

Antworten:

252

Nun, es gibt einige Möglichkeiten, dies je nach beabsichtigtem Verhalten zu tun, aber dieser Link sollte Ihnen die besten Lösungen bieten und stammt nicht überraschend von Dianne Hackborn

http://groups.google.com/group/android-developers/browse_thread/thread/d2a5c203dad6ec42

Im Wesentlichen haben Sie die folgenden Optionen

  • Verwenden Sie einen Namen für Ihren anfänglichen Backstack-Status und verwenden Sie FragmentManager.popBackStack(String name, FragmentManager.POP_BACK_STACK_INCLUSIVE).
  • Verwenden Sie FragmentManager.getBackStackEntryCount()/ getBackStackEntryAt().getId() , um die ID des ersten Eintrags auf dem Backstack abzurufen, und FragmentManager.popBackStack(int id, FragmentManager.POP_BACK_STACK_INCLUSIVE).
  • FragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) soll den gesamten Backstack platzen lassen ... Ich denke die Dokumentation dafür ist einfach falsch. (Eigentlich denke ich, dass es den Fall, in dem Sie vorbeikommen POP_BACK_STACK_INCLUSIVE, einfach nicht abdeckt ),
Joachim
quelle
Was bedeutet das? FragmentManager.getBackStackEntryCount () / getBackStackEntryAt (). GetId ()
dannyroa
4
2. arbeitete für mich. Was es für das Löschen des gesamten Stapels bedeutet: getSupportFragmentManager (). PopBackStack (getSupportFragmentManager (). GetBackStackEntryAt (0) .getId (), FragmentManager.POP_BACK_STACK_INCLUSIVE);
NickL
@JorgeGarcia ist es möglich, dass wir nach dem Popbackstack unser Fragment beenden, ohne das ältere neu zu starten.
Duggu
Bitte beachten Sie, dass es auch die Version popBackStackImmediate () gibt, da popBackStack () asynchron ist. Dies bedeutet, dass die Bereinigung nicht genau zum Zeitpunkt des Aufrufs der Methode erfolgt.
Genc
6
Behauptet, die Gruppe sei als Spam
gesperrt
61

Die andere saubere Lösung, wenn Sie nicht alle Stapeleinträge einfügen möchten ...

getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
getSupportFragmentManager().beginTransaction().replace(R.id.home_activity_container, fragmentInstance).addToBackStack(null).commit();

Dadurch wird zuerst der Stapel bereinigt und dann ein neues Fragment geladen, sodass Sie zu jedem Zeitpunkt nur ein einzelnes Fragment im Stapel haben

Pawan Maheshwari
quelle
1
Das ist sehr schlecht: getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);vor a () zu ersetzen , da sind das Fragment calling Laden onCreateView(), onActivityCreated()etc. Wiederherstellen und verwüstet ein Fragment sofort schlecht. Das Fragment kann beispielsweise einen Empfänger registrieren. Dies wirkt sich auf die Leistung aus.
VasileM
@ VasileM, könnten Sie im Detail beschreiben? In welchen Fällen ist es schlecht? Liegt es an asynhronischem Verhalten von FragmentTransaction? Gibt es nur eine schlechte Leistung des falschen Fragmentstapels?
CoolMind
27

Dank der Antwort von Joachim verwende ich den Code, um alle Backstack-Einträge endgültig zu löschen.

// In your FragmentActivity use getSupprotFragmentManager() to get the FragmentManager.

// Clear all back stack.
int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
for (int i = 0; i < backStackCount; i++) {

    // Get the back stack fragment id.
    int backStackId = getSupportFragmentManager().getBackStackEntryAt(i).getId();

    getSupportFragmentManager().popBackStack(backStackId, 
        FragmentManager.POP_BACK_STACK_INCLUSIVE);

} /* end of for */
Jay Parker
quelle
18
Warum hier eine Schleife verwenden? Für mich hat FragmentManager.popBackStack (null, FragmentManager.POP_BACK_STACK_INCLUSIVE) alle
Marek
3
@Marek, da manchmal nicht alle Fragmente entfernt werden sollen, ist hierfür eine Schleife geeignet.
CoolMind
1
@CoolMind, das macht für mich immer noch keinen Sinn. popBackStack(backStackId, INCLUSIVE)Alle Fragmente (Status) werden wieder in backStackId eingefügt. Sobald Sie also die niedrigste i, die Sie einfügen möchten, einfügen, sollten alle höheren gleichzeitig ausgeblendet werden. Was ist der Sinn einer Schleife?
LarsH
@LarsH, du hast recht. Siehe stackoverflow.com/a/59158254/2914140 . Um andere Fragmente in das gewünschte Tag einzufügen, sollten wir das Fragment zuerst mit addToBackStack ( Tag ) hinzufügen .
CoolMind
7

Ich habe viel nachgeforscht, um Backstack zu bereinigen, und sehe schließlich Transaction BackStack und seine Verwaltung . Hier ist die Lösung, die für mich am besten funktioniert hat.

 // CLEAR BACK STACK.
    private void clearBackStack() {
        final FragmentManager fragmentManager = getSupportFragmentManager();
        while (fragmentManager.getBackStackEntryCount() != 0) {
            fragmentManager.popBackStackImmediate();
        }
    }

Die obige Methode durchläuft alle Transaktionen im Backstack und entfernt sie sofort nacheinander.

Hinweis: Der obige Code funktioniert manchmal nicht und ich stehe wegen dieses Codes vor ANR. Bitte versuchen Sie dies nicht.

Update unten Methode entfernen alle Fregments dieses "Namens" aus dem Backstack.

FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.popBackStack("name",FragmentManager.POP_BACK_STACK_INCLUSIVE);
  • name Wenn nicht null, ist dies der Name eines vorherigen Back-Status, nach dem gesucht werden soll. Wenn gefunden, werden alle Zustände bis zu diesem Zustand gelöscht. Das
  • Mit dem Flag POP_BACK_STACK_INCLUSIVE kann gesteuert werden, ob der benannte Status selbst gelöscht wird. Wenn null, wird nur der oberste Status angezeigt.
Lokesh
quelle
Ich verstehe nicht, warum Sie sie einzeln entfernen würden. Gibt es einen Grund, warum Sie sich für diese Lösung anstelle der anderen Lösungen entschieden haben? Gibt es einen Grund, sie einzeln anstatt auf einmal zu entfernen?
Miva2
Es gibt keine Methode, um den Backstack auf einmal zu entfernen. Siehe developer.android.com/reference/android/app/…
Lokesh
3

Ich verwende einen ähnlichen Code wie diejenigen, die die while-Schleife verwenden, aber ich rufe die Anzahl der Einträge in jeder Schleife auf ... also nehme ich an, dass sie etwas langsamer ist

FragmentManager manager = getFragmentManager();
while (manager.getBackStackEntryCount() > 0){
        manager.popBackStackImmediate();
    }
Jorge Lozano
quelle
0

Wie in Wie man Fragmente aus dem Backstack und von LarsH hier schreibt , können wir mit dieser Methode mehrere Fragmente von oben nach unten in ein bestimmtes Tag ( zusammen mit dem markierten Fragment ) einfügen :

fragmentManager?.popBackStack ("frag", FragmentManager.POP_BACK_STACK_INCLUSIVE);

Ersetzen Sie "frag" durch das Tag Ihres Fragments. Denken Sie daran, dass wir zuerst das Fragment zum Backstack hinzufügen sollten mit:

fragmentTransaction.addToBackStack("frag")

Wenn wir Fragmente mit hinzufügen, werden Fragmente addToBackStack(null)nicht auf diese Weise eingeblendet.

CoolMind
quelle
-4
    // pop back stack all the way
    final FragmentManager fm = getSherlockActivity().getSupportFragmentManager();
    int entryCount = fm.getBackStackEntryCount(); 
    while (entryCount-- > 0) {
        fm.popBackStack();
    }
farid_z
quelle