FragmentManager
ist die Klasse, die für das Ausführen von Aktionen an den Fragmenten Ihrer Anwendung verantwortlich ist, z. B. das Hinzufügen, Entfernen oder Ersetzen und Hinzufügen zum Back Stack.
Wenn Sie die Jetpack Navigation-Bibliothek verwenden, interagieren Sie möglicherweise nie direkt mit FragmentManager
, da sie in Ihrem Namen mit FragmentManager
zusammenarbeitet. Jede Anwendung, die Fragmente verwendet, nutzt jedoch auf einer Ebene FragmentManager
. Es ist daher wichtig, zu verstehen, um was es sich dabei handelt und wie es funktioniert.
Auf dieser Seite werden folgende Themen behandelt:
- Auf
FragmentManager
zugreifen - Die Rolle von
FragmentManager
in Bezug auf deine Aktivitäten und Fragmente. - So verwaltest du den Back Stack mit
FragmentManager
. - So stellen Sie Daten und Abhängigkeiten für Ihre Fragmente bereit.
Auf FragmentManager zugreifen
Sie können über eine Aktivität oder ein Fragment auf das FragmentManager
zugreifen.
FragmentActivity
und die zugehörigen abgeleiteten Klassen wie AppCompatActivity
haben über die Methode getSupportFragmentManager()
Zugriff auf den FragmentManager
.
Fragmente können ein oder mehrere untergeordnete Fragmente hosten. Innerhalb eines Fragments können Sie einen Verweis auf das FragmentManager
abrufen, das die untergeordneten Elemente des Fragments über getChildFragmentManager()
verwaltet.
Wenn Sie auf den Host FragmentManager
zugreifen müssen, können Sie getParentFragmentManager()
verwenden.
Die folgenden Beispiele zeigen die Beziehungen zwischen Fragmenten, ihren Hosts und den jeweiligen FragmentManager
-Instanzen.
Abbildung 1 zeigt zwei Beispiele, von denen jedes einen einzelnen Aktivitätshost hat. Bei der Hostaktivität in beiden Beispielen wird dem Nutzer die Navigation auf oberster Ebene als BottomNavigationView
angezeigt, das dafür verantwortlich ist, das Hostfragment durch andere Bildschirme in der App auszutauschen. Jeder Bildschirm wird als separates Fragment implementiert.
Das Hostfragment in Beispiel 1 hostet zwei untergeordnete Fragmente, die einen Bildschirm mit geteilter Ansicht bilden. Das Hostfragment in Beispiel 2 hostet ein einzelnes untergeordnetes Fragment, das das Anzeigefragment einer Wischansicht darstellt.
Bei dieser Einrichtung können Sie sich jeden Host so vorstellen, als wäre mit einem FragmentManager
ein FragmentManager
verknüpft, der seine untergeordneten Fragmente verwaltet. Das ist in Abbildung 2 mit Property-Zuordnungen zwischen supportFragmentManager
, parentFragmentManager
und childFragmentManager
dargestellt.
Die geeignete FragmentManager
-Eigenschaft, auf die verwiesen werden soll, hängt davon ab, wo sich die Aufrufseite in der Fragmenthierarchie befindet und auf welchen Fragmentmanager Sie zugreifen möchten.
Sobald Sie einen Verweis auf FragmentManager
haben, können Sie damit die Fragmente bearbeiten, die dem Nutzer angezeigt werden.
Untergeordnete Fragmente
Im Allgemeinen besteht Ihre Anwendung aus einer einzelnen oder wenigen Aktivitäten in Ihrem Anwendungsprojekt, wobei jede Aktivität eine Gruppe zusammengehöriger Bildschirme darstellt. Die Aktivität kann einen Punkt zur Platzierung der Navigation auf oberster Ebene sowie einen Ort zur Auswahl von ViewModel
-Objekten und anderen Ansichten zwischen den Fragmenten liefern. Ein Fragment stellt ein einzelnes Ziel in Ihrer Anwendung dar.
Wenn Sie mehrere Fragmente gleichzeitig anzeigen möchten, z. B. in einer geteilten Ansicht oder in einem Dashboard, können Sie untergeordnete Fragmente verwenden, die von Ihrem Zielfragment und seinem untergeordneten Fragmentmanager verwaltet werden.
Weitere Anwendungsfälle für untergeordnete Fragmente sind folgende:
- Bildschirmfolien. Dabei wird ein
ViewPager2
in einem übergeordneten Fragment verwendet, um eine Reihe von untergeordneten Fragmentansichten zu verwalten. - Subnavigation innerhalb einer Reihe zusammengehöriger Bildschirme.
- Jetpack Navigation verwendet untergeordnete Fragmente als einzelne Ziele. Eine Aktivität hostet ein einzelnes übergeordnetes
NavHostFragment
-Element und füllt dessen Bereich mit verschiedenen untergeordneten Zielfragmenten, während Nutzer durch Ihre Anwendung navigieren.
FragmentManager verwenden
FragmentManager
verwaltet den Back-Stack des Fragments. Während der Laufzeit kann das FragmentManager
Back-Stack-Vorgänge wie das Hinzufügen oder Entfernen von Fragmenten als Reaktion auf Nutzerinteraktionen ausführen. Jeder Satz von Änderungen wird in einer einzigen Einheit festgeschrieben, die als FragmentTransaction
bezeichnet wird.
Eine ausführlichere Erläuterung von Fragmenttransaktionen finden Sie im Leitfaden zu Fragmenttransaktionen.
Wenn der Nutzer auf seinem Gerät auf die Schaltfläche „Zurück“ tippt oder wenn Sie FragmentManager.popBackStack()
aufrufen, springt die oberste Fragmenttransaktion aus dem Stapel. Wenn sich keine weiteren Fragmenttransaktionen im Stack befinden und Sie keine untergeordneten Fragmente verwenden, wird das Back-Ereignis bei der Aktivität angezeigt. Wenn Sie untergeordnete Fragmente verwenden, lesen Sie den Abschnitt Besondere Hinweise zu untergeordneten und gleichgeordneten Fragmenten.
Wenn Sie addToBackStack()
für eine Transaktion aufrufen, kann die Transaktion eine beliebige Anzahl von Vorgängen enthalten, z. B. das Hinzufügen mehrerer Fragmente oder das Ersetzen von Fragmenten in mehreren Containern.
Wenn der Back Stack geöffnet wird, werden alle diese Vorgänge zu einer einzigen atomaren Aktion umgekehrt. Wenn Sie jedoch vor dem popBackStack()
-Aufruf zusätzliche Transaktionen festgeschrieben haben und addToBackStack()
für die Transaktion nicht verwendet haben, werden diese Vorgänge nicht rückgängig gemacht. Daher sollten Sie innerhalb einer einzelnen FragmentTransaction
vermeiden, dass Transaktionen, die den Back-Stack beeinträchtigen, mit Transaktionen verschachteln, die dies nicht tun.
Transaktion ausführen
Zum Anzeigen eines Fragments in einem Layoutcontainer verwenden Sie FragmentManager
, um ein FragmentTransaction
zu erstellen. Innerhalb der Transaktion können Sie dann einen add()
- oder replace()
-Vorgang für den Container ausführen.
Ein einfaches FragmentTransaction
-Objekt könnte beispielsweise so aussehen:
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container) setReorderingAllowed(true) addToBackStack("name") // Name can be null }
Java
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null) .setReorderingAllowed(true) .addToBackStack("name") // Name can be null .commit();
In diesem Beispiel ersetzt ExampleFragment
das Fragment, das sich derzeit im Layout-Container befindet, der durch die ID R.id.fragment_container
identifiziert wird. Wenn Sie die Klasse des Fragments für die Methode replace()
bereitstellen, kann FragmentManager
die Instanziierung mit seinem FragmentFactory
verarbeiten.
Weitere Informationen finden Sie im Abschnitt Abhängigkeiten für Fragmente bereitstellen.
setReorderingAllowed(true)
optimiert die Statusänderungen der an der Transaktion beteiligten Fragmente, sodass Animationen und Übergänge korrekt funktionieren. Weitere Informationen zum Navigieren mit Animationen und Übergängen finden Sie unter Fragmenttransaktionen und Mithilfe von Animationen zwischen Fragmenten wechseln.
Durch den Aufruf von addToBackStack()
wird die Transaktion im Back-Stack übergeben. Der Nutzer kann die Transaktion später rückgängig machen und das vorherige Fragment wiederherstellen, indem er auf die Schaltfläche „Zurück“ tippt. Wenn Sie mehrere Fragmente innerhalb einer einzelnen Transaktion hinzugefügt oder entfernt haben, werden alle diese Vorgänge rückgängig gemacht, wenn der Back-Stack per Pop-up entfernt wird. Der optionale Name im addToBackStack()
-Aufruf gibt Ihnen die Möglichkeit, mit popBackStack()
zu einer bestimmten Transaktion zurückzukehren.
Wenn Sie bei der Durchführung einer Transaktion, mit der ein Fragment entfernt wird, nicht addToBackStack()
aufrufen, wird das entfernte Fragment beim Commit der Transaktion gelöscht und der Nutzer kann nicht zu ihm zurückkehren. Wenn Sie beim Entfernen eines Fragments addToBackStack()
aufrufen, lautet das Fragment nur STOPPED
. Wenn der Nutzer zurückkehrt, erhält der Nutzer später den Wert RESUMED
. Seine Ansicht wird in diesem Fall gelöscht. Weitere Informationen finden Sie unter Fragment-Lebenszyklus.
Vorhandenes Fragment suchen
Mit findFragmentById()
können Sie einen Verweis auf das aktuelle Fragment in einem Layoutcontainer abrufen.
Verwenden Sie findFragmentById()
, um ein Fragment entweder anhand der angegebenen ID, wenn es aus XML aufgebläht wird, oder nach der Container-ID, wenn es in einem FragmentTransaction
-Element hinzugefügt wird, zu suchen. Beispiel:
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container) setReorderingAllowed(true) addToBackStack(null) } ... val fragment: ExampleFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) as ExampleFragment
Java
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null) .setReorderingAllowed(true) .addToBackStack(null) .commit(); ... ExampleFragment fragment = (ExampleFragment) fragmentManager.findFragmentById(R.id.fragment_container);
Alternativ können Sie einem Fragment ein eindeutiges Tag zuweisen und mit findFragmentByTag()
eine Referenz abrufen.
Sie können ein Tag mithilfe des XML-Attributs android:tag
bei Fragmenten zuweisen, die in Ihrem Layout oder während eines add()
- oder replace()
-Vorgangs innerhalb einer FragmentTransaction
definiert werden.
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container, "tag") setReorderingAllowed(true) addToBackStack(null) } ... val fragment: ExampleFragment = supportFragmentManager.findFragmentByTag("tag") as ExampleFragment
Java
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null, "tag") .setReorderingAllowed(true) .addToBackStack(null) .commit(); ... ExampleFragment fragment = (ExampleFragment) fragmentManager.findFragmentByTag("tag");
Besondere Hinweise für untergeordnete und gleichgeordnete Fragmente
Es kann jeweils immer nur eine FragmentManager
den Back-Stack des Fragments steuern. Wenn in Ihrer Anwendung mehrere gleichgeordnete Fragmente gleichzeitig auf dem Bildschirm angezeigt werden oder wenn in Ihrer Anwendung untergeordnete Fragmente verwendet werden, wird eine FragmentManager
für die primäre Navigation der Anwendung vorgesehen.
Um das primäre Navigationsfragment innerhalb einer Fragmenttransaktion zu definieren, rufen Sie für die Transaktion die Methode setPrimaryNavigationFragment()
auf und übergeben Sie die Instanz des Fragments, dessen childFragmentManager
das primäre Steuerelement hat.
Betrachten Sie die Navigationsstruktur als eine Reihe von Ebenen, wobei die Aktivität die äußerste Ebene ist, die jede Schicht untergeordneter Fragmente darunter umschließt. Jede Ebene hat ein einzelnes primäres Navigationsfragment.
Wenn das Ereignis „Back“ eintritt, steuert die innerste Ebene das Navigationsverhalten. Sobald die innerste Ebene keine weiteren Fragmenttransaktionen mehr für ein Pop-Back-up hat, kehrt die Kontrolle zur nächsten Ebene zurück und dieser Vorgang wird wiederholt, bis Sie die Aktivität erreichen.
Wenn zwei oder mehr Fragmente gleichzeitig angezeigt werden, ist nur eines davon das primäre Navigationsfragment. Wird ein Fragment als primäres Navigationsfragment festgelegt, wird die Bezeichnung des vorherigen Fragments entfernt. Wenn Sie im vorherigen Beispiel das Detailfragment als primäres Navigationsfragment festlegen, wird die Bezeichnung des Hauptfragments entfernt.
Unterstützung mehrerer Back-Stacks
In einigen Fällen muss Ihre Anwendung möglicherweise mehrere Back-Stacks unterstützen. Ein gängiges Beispiel ist, wenn Ihre Anwendung eine untere Navigationsleiste verwendet. Mit FragmentManager
können mit den Methoden saveBackStack()
und restoreBackStack()
mehrere Back-Stacks unterstützt werden. Mit diesen Methoden können Sie zwischen Back-Stacks wechseln, indem Sie einen Back-Stack speichern und einen anderen wiederherstellen.
saveBackStack()
funktioniert ähnlich wie das Aufrufen von popBackStack()
mit dem optionalen name
-Parameter: Die angegebene Transaktion und alle nachfolgenden Transaktionen im Stapel werden per Pop-up übertragen. Der Unterschied besteht darin, dass saveBackStack()
den Status aller Fragmente in den per Pop-up gespeicherten Transaktionen speichert.
Angenommen, Sie haben dem Back-Stack zuvor ein Fragment hinzugefügt, indem Sie mithilfe von addToBackStack()
ein FragmentTransaction
mit Commit übergeben haben, wie im folgenden Beispiel gezeigt:
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container) setReorderingAllowed(true) addToBackStack("replacement") }
Java
supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null) // setReorderingAllowed(true) and the optional string argument for // addToBackStack() are both required if you want to use saveBackStack() .setReorderingAllowed(true) .addToBackStack("replacement") .commit();
In diesem Fall können Sie diese Fragmenttransaktion und den Status von ExampleFragment
durch Aufrufen von saveBackStack()
speichern:
Kotlin
supportFragmentManager.saveBackStack("replacement")
Java
supportFragmentManager.saveBackStack("replacement");
Sie können restoreBackStack()
mit demselben Namensparameter aufrufen, um alle per Pop-up gespeicherten Transaktionen und alle gespeicherten Fragmentstatus wiederherzustellen:
Kotlin
supportFragmentManager.restoreBackStack("replacement")
Java
supportFragmentManager.restoreBackStack("replacement");
Abhängigkeiten für Fragmente bereitstellen
Beim Hinzufügen eines Fragments können Sie es manuell instanziieren und dem FragmentTransaction
hinzufügen.
Kotlin
fragmentManager.commit { // Instantiate a new instance before adding val myFragment = ExampleFragment() add(R.id.fragment_view_container, myFragment) setReorderingAllowed(true) }
Java
// Instantiate a new instance before adding ExampleFragment myFragment = new ExampleFragment(); fragmentManager.beginTransaction() .add(R.id.fragment_view_container, myFragment) .setReorderingAllowed(true) .commit();
Wenn Sie einen Commit für die Fragmenttransaktion durchführen, wird die Instanz des von Ihnen erstellten Fragments als Instanz verwendet. Während einer Konfigurationsänderung werden jedoch Ihre Aktivität und alle zugehörigen Fragmente gelöscht und dann mit den geeignetsten Android-Ressourcen neu erstellt.
Das FragmentManager
übernimmt dies für Sie: Instanzen Ihrer Fragmente werden neu erstellt, an den Host angehängt und der Back-Stack-Status neu erstellt.
Standardmäßig verwendet FragmentManager
eine FragmentFactory
, die vom Framework zur Instanziierung einer neuen Instanz des Fragments bereitgestellt wird. Diese Standard-Factory verwendet Reflexion, um einen No-Argument-Konstruktor für Ihr Fragment zu finden und aufzurufen. Das bedeutet, dass Sie diese Standard-Factory nicht verwenden können, um Abhängigkeiten für Ihr Fragment bereitzustellen. Das bedeutet auch, dass der benutzerdefinierte Konstruktor, den Sie zum ersten Mal zum Erstellen des Fragments verwendet haben, standardmäßig nicht bei der Neuerstellung verwendet wird.
Wenn Sie Abhängigkeiten für das Fragment bereitstellen oder einen benutzerdefinierten Konstruktor verwenden möchten, erstellen Sie stattdessen eine benutzerdefinierte abgeleitete FragmentFactory
-Klasse und überschreiben dann FragmentFactory.instantiate
.
Anschließend können Sie die Standard-Factory von FragmentManager
durch Ihre benutzerdefinierte Factory überschreiben, die dann zur Instanziierung Ihrer Fragmente verwendet wird.
Angenommen, Sie haben eine DessertsFragment
, die für die Anzeige beliebter Desserts in Ihrer Heimatstadt verantwortlich ist, und dass DessertsFragment
von einer DessertsRepository
-Klasse abhängig ist, die ihr die Informationen bereitstellt, die zum Anzeigen der richtigen UI für den Nutzer erforderlich sind.
Sie können den DessertsFragment
so definieren, dass in seinem Konstruktor eine DessertsRepository
-Instanz erforderlich ist.
Kotlin
class DessertsFragment(val dessertsRepository: DessertsRepository) : Fragment() { ... }
Java
public class DessertsFragment extends Fragment { private DessertsRepository dessertsRepository; public DessertsFragment(DessertsRepository dessertsRepository) { super(); this.dessertsRepository = dessertsRepository; } // Getter omitted. ... }
Eine einfache Implementierung von FragmentFactory
sieht in etwa so aus:
Kotlin
class MyFragmentFactory(val repository: DessertsRepository) : FragmentFactory() { override fun instantiate(classLoader: ClassLoader, className: String): Fragment = when (loadFragmentClass(classLoader, className)) { DessertsFragment::class.java -> DessertsFragment(repository) else -> super.instantiate(classLoader, className) } }
Java
public class MyFragmentFactory extends FragmentFactory { private DessertsRepository repository; public MyFragmentFactory(DessertsRepository repository) { super(); this.repository = repository; } @NonNull @Override public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) { Class<? extends Fragment> fragmentClass = loadFragmentClass(classLoader, className); if (fragmentClass == DessertsFragment.class) { return new DessertsFragment(repository); } else { return super.instantiate(classLoader, className); } } }
Dieses Beispiel erstellt eine abgeleitete Klasse von FragmentFactory
und überschreibt die Methode instantiate()
, um eine benutzerdefinierte Logik zur Fragmenterstellung für einen DessertsFragment
bereitzustellen.
Andere Fragmentklassen werden vom Standardverhalten von FragmentFactory
bis super.instantiate()
verarbeitet.
Sie können dann MyFragmentFactory
als Factory festlegen, die beim Erstellen der Fragmente Ihrer Anwendung verwendet werden soll. Dazu legen Sie eine Eigenschaft für FragmentManager
fest. Sie müssen diese Eigenschaft vor dem super.onCreate()
Ihrer Aktivität festlegen, damit MyFragmentFactory
bei der Neuerstellung der Fragmente verwendet wird.
Kotlin
class MealActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { supportFragmentManager.fragmentFactory = MyFragmentFactory(DessertsRepository.getInstance()) super.onCreate(savedInstanceState) } }
Java
public class MealActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { DessertsRepository repository = DessertsRepository.getInstance(); getSupportFragmentManager().setFragmentFactory(new MyFragmentFactory(repository)); super.onCreate(savedInstanceState); } }
Wenn Sie FragmentFactory
in der Aktivität festlegen, wird die Fragmenterstellung in der gesamten Fragmenthierarchie der Aktivität überschrieben. Mit anderen Worten, der childFragmentManager
aller untergeordneten Fragmente, die Sie hinzufügen, verwendet die hier festgelegte benutzerdefinierte Fragment-Factory, sofern diese nicht auf einer niedrigeren Ebene überschrieben wird.
Mit FragmentFactory testen
Testen Sie die Fragmente in einer Architektur mit einer einzelnen Aktivität isoliert mithilfe der Klasse FragmentScenario
. Da Sie sich nicht auf die benutzerdefinierte onCreate
-Logik Ihrer Aktivität verlassen können, können Sie stattdessen FragmentFactory
als Argument an Ihren Fragmenttest übergeben, wie im folgenden Beispiel gezeigt:
// Inside your test val dessertRepository = mock(DessertsRepository::class.java) launchFragment<DessertsFragment>(factory = MyFragmentFactory(dessertRepository)).onFragment { // Test Fragment logic }
Ausführliche Informationen zu diesem Testprozess und vollständige Beispiele finden Sie unter Fragmente testen.