FragmentManager
è
la classe responsabile dell'esecuzione di azioni sui frammenti della tua app, ad esempio
come aggiungerli, rimuoverli o sostituirli e aggiungerli allo stack posteriore.
Potresti non interagire mai direttamente con FragmentManager
se usi
alla libreria Jetpack Navigation, in quanto compatibile con le
FragmentManager
per tuo conto. Tuttavia, qualsiasi app che utilizza frammenti
usare FragmentManager
a un certo livello, quindi è importante capire
è e come funziona.
Questa pagina riguarda:
- Come accedere all'
FragmentManager
. - Il ruolo di
FragmentManager
in relazione alle tue attività e ai tuoi frammenti. - Come gestire il back stack con
FragmentManager
. - Come fornire dati e dipendenze ai tuoi frammenti.
Accedi a FragmentManager
Puoi accedere a FragmentManager
da un'attività o da un frammento.
FragmentActivity
e le sue sottoclassi, come
AppCompatActivity
,
avranno accesso al FragmentManager
tramite
getSupportFragmentManager()
.
I frammenti possono ospitare uno o più frammenti figlio. Interno
un frammento, puoi ottenere un riferimento al FragmentManager
che gestisce
i figli del frammento attraverso
getChildFragmentManager()
Se devi accedere all'host FragmentManager
, puoi utilizzare
getParentFragmentManager()
Ecco un paio di esempi per vedere le relazioni tra
frammenti, i relativi host e le istanze FragmentManager
associate
con ciascuno.
La figura 1 mostra due esempi, ognuno dei quali ha un singolo host di attività. La
l'attività dell'host in entrambi questi esempi mostra la navigazione di primo livello per
l'utente in qualità di utente
BottomNavigationView
responsabile dello scambio del frammento host con
schermate dell'app. Ogni schermata è implementata come frammento separato.
Il frammento host nell'esempio 1 ospita due frammenti figlio che che compongono uno schermo in visualizzazione divisa. Il frammento host nell'esempio 2 ospita un frammento singolo che compone il frammento visualizzato di un visualizzazione scorrimento.
Data la configurazione, puoi considerare ogni host come se avesse un FragmentManager
associate per gestire i frammenti figlio. Questo è illustrato in
la figura 2 insieme alle mappature di proprietà tra supportFragmentManager
,
parentFragmentManager
e childFragmentManager
.
La proprietà FragmentManager
appropriata a cui fare riferimento dipende da dove
callsite si trova nella gerarchia dei frammenti insieme al gestore dei frammenti
a cui stai tentando di accedere.
Una volta ottenuto un riferimento a FragmentManager
, puoi utilizzarlo per
e manipolare i frammenti mostrati all'utente.
Frammenti figlio
In generale, l'app è composta da un solo numero o da un numero ridotto
di attività nel progetto dell'applicazione, dove ogni attività rappresenta
un gruppo di schermate correlate. L'attività può fornire un punto di riferimento
navigazione di primo livello e una posizione per definire l'ambito di ViewModel
oggetti e altri stati di visualizzazione
tra i frammenti. Un frammento rappresenta una singola destinazione nel
dell'app.
Se vuoi mostrare più frammenti contemporaneamente, ad esempio in una visualizzazione divisa o una dashboard, puoi utilizzare i frammenti secondari gestiti del frammento di destinazione e il relativo gestore dei frammenti figlio.
Altri casi d'uso per i frammenti figlio sono i seguenti:
- Diapositive sulle schermate,
utilizzando
ViewPager2
in un frammento padre per gestire una serie di elementi figlio delle immagini frammentarie. - Navigazione secondaria all'interno di una serie di schermate correlate.
- Jetpack Navigation utilizza frammenti figlio come singole destinazioni. Un
l'attività ospita un unico
NavHostFragment
principale e riempie il relativo spazio con diversi frammenti di destinazione figlio man mano che gli utenti la tua app.
Utilizzare FragmentManager
FragmentManager
gestisce il back stack dei frammenti. In fase di runtime,
FragmentManager
può eseguire operazioni sullo stack posteriore come l'aggiunta o la rimozione
in risposta alle interazioni degli utenti. Ogni insieme di modifiche
impegnate insieme in una singola unità chiamata
FragmentTransaction
Per una discussione più approfondita sulle transazioni con frammenti, consulta
guida alle transazioni di frammento.
Quando l'utente tocca il pulsante Indietro sul dispositivo o quando chiami
FragmentManager.popBackStack()
,
la transazione con il frammento più in alto esce dallo stack. Se non sono presenti altri frammenti
transazioni in stack e, se non utilizzi frammenti, l'oggetto
l'evento compare con l'attività. Se utilizzi frammenti figlio, consulta
considerazioni speciali per i frammenti figlio e di pari livello.
Quando chiami
addToBackStack()
in una transazione, la transazione può includere un numero qualsiasi
operazioni come l'aggiunta di più frammenti o la sostituzione di frammenti in più
containerizzati.
Quando il pila posteriore è scoppiato,
le operazioni si invertono come una singola azione atomica. Tuttavia, se ti impegni a
ulteriori transazioni prima della chiamata popBackStack()
e se
non ha utilizzato addToBackStack()
per la transazione, queste operazioni
non invertire. Pertanto, all'interno di un singolo FragmentTransaction
, evita
interfoliando le transazioni che interessano il back stack con quelle che non lo fanno.
Esecuzione di una transazione
Per visualizzare un frammento all'interno di un contenitore di layout, utilizza FragmentManager
per creare un FragmentTransaction
. All'interno della transazione, puoi quindi
esegui un
add()
oppure replace()
dell'operazione sul container.
Ad esempio, un FragmentTransaction
semplice potrebbe avere il seguente aspetto:
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 questo esempio, ExampleFragment
sostituisce l'eventuale frammento, ovvero
attualmente nel contenitore di layout identificato
ID R.id.fragment_container
. Fornire la classe del frammento all'oggetto
replace()
consente a FragmentManager
di gestire l'istanza utilizzando il suo
FragmentFactory
.
Per saperne di più, consulta Fornire dipendenze per i frammenti
.
setReorderingAllowed(true)
ottimizza le modifiche di stato dei frammenti coinvolti nella transazione
per far sì che le animazioni e le transizioni
funzionino correttamente. Per ulteriori informazioni
navigare con animazioni e transizioni, vedi
Transazioni frammentarie e
Spostati tra i frammenti utilizzando le animazioni.
Chiamata in corso
addToBackStack()
esegue il commit della transazione nel back stack. L'utente può in seguito invertire
transazione e recupera il frammento precedente toccando il pulsante Indietro
. Se hai aggiunto o rimosso più frammenti all'interno di un singolo
transazione, tutte queste operazioni vengono annullate quando
diventa uno scoppiettante. Il nome facoltativo fornito nella chiamata a addToBackStack()
fornisce
puoi tornare a una transazione specifica utilizzando
popBackStack()
Se non chiami addToBackStack()
quando esegui una transazione che
rimuove un frammento, che poi viene distrutto quando
viene eseguito il commit della transazione e l'utente non può accedervi. Se
chiama addToBackStack()
quando rimuovi un frammento,
solo STOPPED
e sarà RESUMED
quando l'utente torna indietro. La sua visuale
è eliminata in questo caso. Per ulteriori informazioni, vedi
Ciclo di vita dei frammenti.
Trova un frammento esistente
Puoi ottenere un riferimento al frammento corrente all'interno di un contenitore di layout
utilizzando
findFragmentById()
Usa findFragmentById()
per cercare un frammento in base all'ID specificato quando
generato in modo artificioso da XML o dall'ID container se aggiunto in un
FragmentTransaction
. Ecco un esempio:
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);
In alternativa, puoi assegnare un tag univoco a un frammento e ottenere un
riferimento utilizzando
findFragmentByTag()
Puoi assegnare un tag utilizzando l'attributo XML android:tag
sui frammenti che
vengono definiti all'interno del layout o durante un evento add()
o replace()
all'interno di un 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");
Considerazioni speciali per i frammenti figlio e di pari livello
Solo un FragmentManager
può controllare il back stack del frammento
in qualsiasi momento. Se la tua app mostra più frammenti di pari livello nella
schermata contemporaneamente o, se la tua app utilizza frammenti figlio,
FragmentManager
è progettato per gestire la navigazione principale dell'app.
Per definire il frammento di navigazione principale all'interno di una transazione di frammento,
chiama il
setPrimaryNavigationFragment()
sulla transazione, passando l'istanza del frammento la cui
childFragmentManager
ha il controllo principale.
Considera la struttura di navigazione come una serie di livelli, con l'attività come strato più esterno, avvolgendo al di sotto ogni strato di frammenti figlio. Ogni livello ha un singolo frammento di navigazione principale.
Quando la schiena , il livello più interno controlla il comportamento di navigazione. Una volta lo strato più interno non ha più transazioni di frammenti da cui recuperare, il controllo ritorna allo strato successivo e questo processo si ripete finché raggiungere l'attività.
Quando due o più frammenti vengono visualizzati contemporaneamente, uno di questi è il frammento di navigazione principale. Impostazione di un frammento poiché il frammento di navigazione principale rimuove la designazione dalla precedente . Utilizzando l'esempio precedente, se imposti il frammento di dettaglio come frammento di navigazione principale, la designazione del frammento principale viene rimossa.
Supporta più back stack
In alcuni casi, la tua app potrebbe dover supportare più back stack. Un comune
un esempio è l'uso di una barra di navigazione
in basso per l'app. FragmentManager
consente
supporta più back stack con saveBackStack()
e
restoreBackStack()
. Questi metodi ti consentono di passare da una modalità all'altra
salvando uno stack arretrato e ripristinandone uno diverso.
saveBackStack()
funziona in modo simile alla chiamata di popBackStack()
con il
Parametro name
: la transazione specificata e tutte le transazioni successive alla data
vengono compilate in modalità popup. La differenza è che saveBackStack()
salva
stato di tutti i frammenti nello stato
transazioni.
Ad esempio, supponiamo che in precedenza tu abbia aggiunto un frammento allo stack posteriore
eseguendo il commit di un FragmentTransaction
utilizzando addToBackStack()
, come mostrato in
nell'esempio seguente:
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 questo caso, puoi salvare la transazione di frammento e lo stato
ExampleFragment
chiamando il numero saveBackStack()
:
Kotlin
supportFragmentManager.saveBackStack("replacement")
Java
supportFragmentManager.saveBackStack("replacement");
Puoi chiamare restoreBackStack()
con lo stesso parametro nome per ripristinare tutti
le transazioni popolate e tutti gli stati dei frammenti salvati:
Kotlin
supportFragmentManager.restoreBackStack("replacement")
Java
supportFragmentManager.restoreBackStack("replacement");
Fornisci dipendenze ai tuoi frammenti
Quando aggiungi un frammento, puoi creare un'istanza del frammento manualmente,
aggiungilo a FragmentTransaction
.
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();
Quando esegui il commit della transazione del frammento, l'istanza del frammento
che hai creato è l'istanza utilizzata. Tuttavia, durante un
modifica della configurazione,
dell'attività e tutti i suoi frammenti vengono distrutti e quindi ricreati
il più applicabile
Risorse Android.
FragmentManager
gestisce tutto questo per te: ricrea le istanze
dei tuoi frammenti, li collega all'host e ricrea il back stack
stato.
Per impostazione predefinita, FragmentManager
utilizza un
FragmentFactory
che
il framework fornisce per creare un'istanza di una nuova istanza del frammento. Questo
La fabbrica predefinita usa la riflessione per trovare e richiamare un costruttore senza argomento.
per il frammento. Ciò significa che non puoi utilizzare questa fabbrica predefinita
fornire dipendenze al frammento. Significa anche che qualsiasi
il costruttore che hai usato per creare il frammento la prima volta non viene usato
durante la ricreazione.
Per fornire dipendenze al frammento o usare qualsiasi tipo di
costruttore, crea invece una sottoclasse FragmentFactory
personalizzata
e poi sostituisci
FragmentFactory.instantiate
Puoi quindi eseguire l'override del valore di fabbrica predefinito di FragmentManager
con
personalizzata, che viene poi utilizzata per creare un'istanza dei frammenti.
Supponiamo che tu abbia un DessertsFragment
responsabile della visualizzazione
dolci popolari nella tua città e DessertsFragment
ha una dipendenza da una classe DessertsRepository
che le fornisce
le informazioni necessarie per mostrare all'utente l'interfaccia utente corretta.
Puoi definire DessertsFragment
in modo che richieda un DessertsRepository
nel suo costruttore.
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. ... }
Una semplice implementazione di FragmentFactory
potrebbe essere simile alla seguente:
per eseguire le operazioni indicate di seguito.
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); } } }
Questo esempio esegue le sottoclassi FragmentFactory
, eseguendo l'override di instantiate()
per fornire una logica di creazione dei frammenti personalizzata per un DessertsFragment
.
Altre classi di frammenti sono gestite dal comportamento predefinito di
Da FragmentFactory
a super.instantiate()
.
Puoi quindi designare MyFragmentFactory
come fabbrica da utilizzare quando
creare i frammenti della tua app impostando una proprietà
FragmentManager
. Devi impostare questa proprietà prima dell'inizio
super.onCreate()
per garantire che MyFragmentFactory
venga utilizzato quando
a ricreare i frammenti.
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); } }
L'impostazione di FragmentFactory
nell'attività sostituisce il frammento
creazione in tutta la gerarchia dei frammenti dell'attività. In altre parole,
il valore childFragmentManager
di tutti i frammenti secondari che aggiungi utilizza il parametro
valore di fabbrica del frammento impostato qui, a meno che non venga sostituito a un livello inferiore.
Esegui test con FragmentFA
In un'architettura a singola attività, testa i tuoi frammenti in
utilizzando
FragmentScenario
. Poiché non puoi fare affidamento sulla logica onCreate
personalizzata del tuo
attività, puoi passare il valore FragmentFactory
come argomento
al test dei frammenti, come mostrato nell'esempio seguente:
// Inside your test val dessertRepository = mock(DessertsRepository::class.java) launchFragment<DessertsFragment>(factory = MyFragmentFactory(dessertRepository)).onFragment { // Test Fragment logic }
Per informazioni dettagliate su questo processo di test e per esempi completi, consulta Testare i frammenti.