Fragment manager

FragmentManager ist Die Klasse, die für die Durchführung von Aktionen an den App-Fragmenten verantwortlich ist, z. B. hinzufügen, entfernen oder ersetzen und sie dem Back-Stack hinzufügen.

Sie interagieren möglicherweise nie direkt mit FragmentManager, wenn Sie Jetpack Navigation-Bibliothek, da sie mit der FragmentManager in deinem Namen. Jede Anwendung, die Fragmente verwendet, FragmentManager verwenden. Es ist daher wichtig zu verstehen, wie es ist und wie es funktioniert.

Auf dieser Seite werden folgende Themen behandelt:

  • So greifen Sie auf die FragmentManager zu.
  • Die Rolle von FragmentManager in Bezug auf deine Aktivitäten und Fragmente.
  • So verwalten Sie den Back-Stack mit FragmentManager.
  • So stellen Sie Daten und Abhängigkeiten für Ihre Fragmente bereit.

Auf FragmentManager zugreifen

Du kannst über eine Aktivität oder ein Fragment auf das FragmentManager zugreifen.

FragmentActivity und ihre abgeleiteten Klassen, z. B. AppCompatActivity, haben über das FragmentManager getSupportFragmentManager() .

Fragmente können ein oder mehrere untergeordnete Fragmente hosten. Innenansicht ein Fragment, können Sie einen Verweis auf das FragmentManager abrufen, das die untergeordneten Elemente des Fragments durch getChildFragmentManager() Wenn Sie auf den Host FragmentManager zugreifen müssen, können Sie Folgendes verwenden: getParentFragmentManager()

Die folgenden Beispiele veranschaulichen die Zusammenhänge zwischen Fragmente, ihre Hosts und die zugeordneten FragmentManager-Instanzen mit jedem Bild.

<ph type="x-smartling-placeholder">
</ph> Zwei Beispiele für das UI-Layout, die die Beziehungen zwischen
            Fragmente und ihre Hostaktivitäten
Abbildung 1: Zwei Beispiele für das UI-Layout, die das Beziehungen zwischen Fragmenten und ihren Hostaktivitäten.

Abbildung 1 zeigt zwei Beispiele, von denen jedes einen einzelnen Aktivitätshost hat. Die „Host-Aktivität“ in diesen beiden Beispielen zeigt die Navigation der obersten Ebene zu Nutzende als BottomNavigationView das für den Austausch des Hostfragments Bildschirmen in der App. Jeder Bildschirm wird als separates Fragment implementiert.

Das Hostfragment in Beispiel 1 hostet zwei untergeordnete Fragmente, Split View erstellen. Das Hostfragment in Beispiel 2 hostet ein einzelnes untergeordnetes Fragment, das das Anzeigefragment eines Ansicht wischen.

Angesichts dieser Konfiguration können Sie sich jeden Host als einen FragmentManager vorstellen zur Verwaltung der untergeordneten Fragmente. Dies wird in Abbildung 2 zusammen mit Property-Zuordnungen zwischen supportFragmentManager, parentFragmentManager und childFragmentManager.

<ph type="x-smartling-placeholder">
</ph> jedem Host ist ein eigener FragmentManager zugeordnet
            zum Verwalten der untergeordneten Fragmente
Abbildung 2: Jeder Host hat eine eigene FragmentManager ist mit ihr verknüpft, das verwaltet untergeordneten Fragmenten.

Auf welches FragmentManager-Attribut verwiesen werden soll, hängt davon ab, die Aufrufwebsite befindet sich in der Fragmenthierarchie und in welchem Fragmentmanager auf die Sie zugreifen möchten.

Sobald du einen Verweis auf FragmentManager hast, kannst du damit Folgendes tun: die dem Nutzer angezeigten Fragmente zu manipulieren.

Untergeordnete Fragmente

Im Allgemeinen besteht Ihre App aus einer einzigen oder der Aktivitäten in Ihrem Anwendungsprojekt, wobei jede Aktivität für zusammengehörigen Bildschirmen. Die Aktivität könnte einen Anhaltspunkt dafür liefern, Navigation der obersten Ebene und ein Ort, an dem Sie ViewModel-Objekte und andere Ansichtsstatus festlegen können zwischen den Fragmenten. Ein Fragment stellt ein einzelnes Ziel in Ihrer

Wenn Sie mehrere Fragmente gleichzeitig anzeigen möchten, beispielsweise in einer geteilten Ansicht, oder ein Dashboard, können Sie untergeordnete Fragmente verwenden, die von Ihrem Zielfragment und sein untergeordneter Fragmentmanager.

Weitere Anwendungsfälle für untergeordnete Fragmente:

  • Bildschirmfolien, Verwenden einer ViewPager2 in einem übergeordneten Fragment, um eine Reihe untergeordneter Elemente zu verwalten Fragment-Ansichten.
  • Subnavigation innerhalb einer Reihe zusammengehöriger Bildschirme.
  • Jetpack Navigation verwendet untergeordnete Fragmente als einzelne Ziele. Eine wird für die Aktivität ein einzelnes übergeordnetes Element (NavHostFragment) gehostet und der entsprechende Bereich wird ausgefüllt. mit unterschiedlichen untergeordneten Zielfragmenten für Ihre App.

FragmentManager verwenden

Der FragmentManager verwaltet den Back-Stack des Fragments. Während der Laufzeit FragmentManager kann Back-Stack-Vorgänge wie das Hinzufügen oder Entfernen ausführen auf Fragmente reagieren. Jede Gruppe von Änderungen als eine Einheit, die als FragmentTransaction Eine ausführlichere Diskussion über Fragmenttransaktionen finden Sie in der Transaktionsleitfaden für Fragmente nutzen

Wenn der Nutzer auf seinem Gerät auf die Schaltfläche „Zurück“ tippt oder wenn du anrufst FragmentManager.popBackStack(), hebt sich die oberste Fragmenttransaktion vom Stack ab. Wenn es kein Fragment mehr gibt Transaktionen im Stapel. Falls Sie keine untergeordneten Fragmente verwenden, erscheint als Nächstes in der Aktivität. Wenn Sie untergeordnete Fragmente verwenden, lesen Sie den Abschnitt Besondere Überlegungen für untergeordnete und gleichgeordnete Fragmente.

Wenn du anrufst addToBackStack() für eine Transaktion gilt, kann sie eine beliebige Anzahl von wie das Hinzufügen mehrerer Fragmente oder das Ersetzen von Fragmenten in mehreren Container.

Wenn der Back Stack platzt, Operationen umgekehrt als eine einzelne atomare Aktion. Wenn Sie jedoch vor dem popBackStack()-Anruf weitere Transaktionen vorgenommen und addToBackStack() nicht für die Transaktion verwendet haben, sind diese Vorgänge nicht rückgängig machen. Vermeiden Sie daher innerhalb einer einzelnen FragmentTransaction und verschränkt Transaktionen, die sich auf den Back-Stack auswirken, mit solchen, die dies nicht tun.

Transaktion ausführen

Um ein Fragment innerhalb eines Layoutcontainers anzuzeigen, verwenden Sie die Methode FragmentManager. um ein FragmentTransaction zu erstellen. Innerhalb der Transaktion können Sie eine add() oder replace() für den Container ausgeführt wird.

Ein einfaches FragmentTransaction 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, sofern vorhanden. sich derzeit im Layout-Container befindet, der vom R.id.fragment_container-ID. Bereitstellung der Klasse des Fragments für die replace() die Instanziierung mithilfe der Methode FragmentManager FragmentFactory Weitere Informationen finden Sie unter Abhängigkeiten für Fragmente bereitstellen. .

setReorderingAllowed(true) optimiert die Statusänderungen der an der Transaktion beteiligten Fragmente damit Animationen und Übergänge korrekt funktionieren. Weitere Informationen zu Animationen und Übergänge nutzen, Transaktionen fragmentieren und Mithilfe von Animationen zwischen Fragmenten wechseln

Anrufen addToBackStack() überträgt die Transaktion im Back-Stack. Der Nutzer kann die und das vorherige Fragment wiederherstellen, indem Sie auf die Schaltfläche Schaltfläche. Wenn Sie mehrere Fragmente innerhalb eines werden alle Vorgänge rückgängig gemacht, wird geplatzt. Mit dem optionalen Namen, der im addToBackStack()-Aufruf angegeben wird, können Sie mit der Funktion popBackStack()

Wenn Sie addToBackStack() nicht aufrufen, wenn Sie eine Transaktion ausführen, entfernt ein Fragment, wird das entfernte Fragment gelöscht, Für die Transaktion wurde ein Commit durchgeführt und der Nutzer kann nicht zu ihr zurückkehren. Wenn Sie addToBackStack() aufrufen, wenn ein Fragment entfernt wird, dann wird das Fragment nur STOPPED und später RESUMED, wenn der Nutzer zurücknavigiert. Ansicht is gelöscht wurde. Weitere Informationen finden Sie unter Lebenszyklus des Fragments:

Vorhandenes Fragment suchen

Sie können einen Verweis auf das aktuelle Fragment innerhalb eines Layoutcontainers abrufen mit findFragmentById() Verwenden Sie findFragmentById(), um ein Fragment entweder über die angegebene ID zu suchen, wenn aus XML oder mit der Container-ID beim Hinzufügen FragmentTransaction. 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 eine Referenz mithilfe von findFragmentByTag() Du kannst einem Fragment mithilfe des XML-Attributs android:tag ein Tag zuweisen, werden in Ihrem Layout oder während eines add()- oder replace()-Vorgangs innerhalb einer FragmentTransaction.

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 Überlegungen für untergeordnete und gleichgeordnete Fragmente

Der Back-Stack des Fragments kann nur von einer einzigen FragmentManager gesteuert werden jederzeit ändern. Wenn in Ihrer App mehrere gleichgeordnete Fragmente oder wenn deine App untergeordnete Fragmente verwendet, FragmentManager ist für die primäre Navigation deiner App vorgesehen.

Um das primäre Navigationsfragment innerhalb einer Fragmenttransaktion zu definieren, ruf die setPrimaryNavigationFragment() -Methode für die Transaktion, wobei die Instanz des Fragments übergeben wird, dessen childFragmentManager hat die primäre Kontrolle.

Betrachten Sie die Navigationsstruktur als eine Reihe von Ebenen, mit der Aktivität als äußerster Layer und umschließt jede darunterliegende Ebene mit untergeordneten Fragmenten. Jede Ebene hat ein einzelnes primäres Navigationsfragment.

Wenn der Rücken -Ereignis auftritt, steuert die innerste Ebene das Navigationsverhalten. Sobald die die innerste Schicht keine Fragmenttransaktionen mehr enthält, Die Steuerung kehrt zum nächsten Layer zurück. Dieser Vorgang wiederholt sich, bis Sie um die Aktivität zu erreichen.

Wenn zwei oder mehr Fragmente gleichzeitig angezeigt werden, eines davon ist das primäre Navigationsfragment. Fragment festlegen da das primäre Navigationsfragment die Kennzeichnung aus dem vorherigen Fragment. Wenn Sie wie im vorherigen Beispiel das Detailfragment als das primäres Navigationsfragment entfernt, wird die Bezeichnung des Hauptfragments entfernt.

Unterstützung mehrerer Back Stacks

In einigen Fällen muss Ihre App möglicherweise mehrere Back Stacks unterstützen. Eine gemeinsame Beispiel: Ihre App verwendet eine Navigationsleiste am unteren Rand. Mit FragmentManager lassen Sie unterstützen mehrere Back Stacks mit saveBackStack() und restoreBackStack()-Methoden. Mit diesen Methoden können Sie zwischen indem Sie einen Back Stack speichern und einen anderen wiederherstellen.

saveBackStack() funktioniert ähnlich wie der Aufruf von popBackStack() mit der optionalen Parameter name: die angegebene Transaktion und alle darauffolgenden Transaktionen im Stacks platzen. Der Unterschied besteht darin, dass saveBackStack() die aller Fragmente im Pop-up- Transaktionen.

Angenommen, Sie haben dem Back Stack zuvor ein Fragment hinzugefügt, Einen Commit eines FragmentTransaction mit addToBackStack() durchführen, wie im folgendes Beispiel:

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 ExampleFragment durch Aufrufen von saveBackStack():

Kotlin

supportFragmentManager.saveBackStack("replacement")

Java

supportFragmentManager.saveBackStack("replacement");

Sie können restoreBackStack() mit demselben Namensparameter aufrufen, um alle die per POP heruntergeladenen Transaktionen und alle gespeicherten Fragmentstatus:

Kotlin

supportFragmentManager.restoreBackStack("replacement")

Java

supportFragmentManager.restoreBackStack("replacement");

Abhängigkeiten zu Ihren Fragmenten bereitstellen

Beim Hinzufügen eines Fragments können Sie es manuell instanziieren und Fügen Sie es dem FragmentTransaction hinzu.

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 Fragments die Sie erstellt haben, ist die verwendete Instanz. Während eines Konfigurationsänderung haben, und all ihre Fragmente werden zerstört und dann mit die am besten geeignete Android-Ressourcen FragmentManager übernimmt all dies für Sie: erstellt Instanzen neu Ihrer Fragmente, verbindet sie mit dem Host und erstellt den Back Stack neu Bundesstaat.

Standardmäßig verwendet der FragmentManager einen FragmentFactory, die das Framework bietet, um eine neue Instanz Ihres Fragments zu instanziieren. Dieses Die Standard-Factory verwendet Reflexion, um einen No-Argument-Konstruktor zu finden und aufzurufen für das Fragment. Das bedeutet, dass Sie diese Standard-Werkseinstellungen nicht verwenden können, Abhängigkeiten zum Fragment bereitstellen. Es bedeutet auch, dass alle benutzerdefinierten Konstruktor, mit dem Sie das Fragment erstmalig erstellt haben, wird nicht verwendet. bei der Neuerstellung standardmäßig aktiviert.

Zur Bereitstellung von Abhängigkeiten für Ihr Fragment oder zur Verwendung eines benutzerdefinierten -Konstruktor erstellen, erstellen Sie stattdessen eine benutzerdefinierte FragmentFactory-Unterklasse und dann FragmentFactory.instantiate Anschließend können Sie die standardmäßige Factory von FragmentManager mit Ihre benutzerdefinierte Factory, mit der Ihre Fragmente instanziiert werden.

Angenommen, Sie haben ein DessertsFragment, das für die Darstellung beliebte Desserts in Ihrer Heimatstadt, und dass DessertsFragment Abhängigkeit von einer DessertsRepository-Klasse, die sie die Informationen benötigt, um Ihren Nutzern die richtige UI anzuzeigen.

Sie können festlegen, dass für Ihre DessertsFragment ein DessertsRepository erforderlich ist. -Instanz in ihrem Konstruktor befinden.

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 Ihrer FragmentFactory könnte etwa so aussehen: Folgendes:

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

In diesem Beispiel wird FragmentFactory abgeleitet, wodurch instantiate() überschrieben wird. , um eine benutzerdefinierte Logik zur Fragmenterstellung für DessertsFragment bereitzustellen. Andere Fragmentklassen werden vom Standardverhalten von FragmentFactory bis super.instantiate().

Sie können dann MyFragmentFactory als Werkseinstellung festlegen, die verwendet wird, wenn App-Fragmente durch Festlegen einer Eigenschaft für die FragmentManager. Sie müssen diese Eigenschaft vor dem super.onCreate(), damit MyFragmentFactory verwendet wird, wenn die Reproduktion der Fragmente.

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

Festlegen von FragmentFactory im Aktivitätsüberschreibungs-Fragment in der gesamten Fragmenthierarchie der Aktivität erstellt. Mit anderen Worten: Für childFragmentManager aller hinzugefügten untergeordneten Fragmente wird das benutzerdefinierte Das Fragment kann hier festgelegt werden, es sei denn, es wird auf einer niedrigeren Ebene überschrieben.

Mit FragmentFactory testen

Testen Sie Ihre Fragmente in einer Architektur mit einer einzelnen Aktivität in Isolierung mithilfe der Methode FragmentScenario . Da Sie sich nicht auf die benutzerdefinierte onCreate-Logik Ihres Aktivität ausführen, können Sie stattdessen FragmentFactory als Argument übergeben. dem Fragmenttest hinzugefügt, 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 über diesen Testprozess und vollständige Beispiele Siehe Fragmente testen.