Scopri i concetti chiave di FrameLayout — la ViewGroup più semplice nell'SDK Android, progettata per ospitare un singolo elemento figlio (di solito) o sovrapporre più elementi uno sull'altro. FrameLayout è utile per contenitori segnaposto, frammenti, indicatori di caricamento ed elementi in primo piano. Non gestisce il posizionamento dei figli — ogni elemento successivo viene disegnato sopra il precedente e la loro posizione è determinata da layout_gravity (left, top, right, bottom, center). Gli scenari di base sono descritti nel Riferimento API FrameLayout.
Punti chiave
FrameLayout è la ViewGroup più semplice nell'SDK Android, progettata per bloccare un'area dello schermo e visualizzare una singola vista figlia (o sovrapporne diverse). A differenza di LinearLayout (disposizione sequenziale) e RelativeLayout (posizionamento relativo), FrameLayout non modifica le posizioni dei figli — ogni nuovo figlio viene posizionato nell'angolo superiore sinistro (0,0) per impostazione predefinita e disegnato sopra il precedente.
FrameLayout è stato introdotto nell'API Level 1 e rimane il contenitore Android più leggero: non sovrascrive onMeasure con logica complessa ed esegue un numero minimo di operazioni di layout. Secondo Android Performance Blog, FrameLayout esegue onLayout in un unico passaggio e non aggiunge praticamente alcun overhead rispetto al posizionamento diretto di una View. Questo lo rende una scelta ideale per i contenitori dove la velocità è importante: layout di elementi RecyclerView (in combinazione con ConstraintLayout per il posizionamento), contenitori di Fragment, livelli di sovrapposizione.
La dimensione di FrameLayout per impostazione predefinita è determinata dall'elemento figlio più grande (se match_parent non è impostato). Se non viene specificato alcun figlio, FrameLayout si riduce a (0,0). L'attributo android:measureAllChildren (sezione sottostante) modifica questo comportamento.
FrameLayout eredita da ViewGroup ed è il genitore diretto di molti contenitori specializzati: FragmentContainerView (frammenti), CardView (schede con ombra), ScrollView (figlio singolo), NestedScrollView. Quando si crea un'Activity con navigazione tramite Fragment, il modello standard di Android Studio utilizza FrameLayout (o FragmentContainerView) come contenitore radice per i frammenti.
android:foreground — un attributo di FrameLayout che specifica un drawable disegnato sopra tutti gli elementi figli. A differenza di background (sotto i figli), foreground viene visualizzato sopra il contenuto e può essere trasparente. Viene utilizzato per: effetti di sovrapposizione al tocco (ripple tramite ?attr/selectableItemBackground), mascheramento di immagini, visualizzazione di indicatori di stato (segno di spunta su un'immagine).
Foreground supporta le risorse drawable standard: ColorDrawable, ShapeDrawable, RippleDrawable, LayerDrawable. Dall'API 23+, android:foregroundGravity è disponibile per il posizionamento del primo piano (fill, center, top, bottom). In modalità fill, foreground si estende su tutto FrameLayout; in modalità center, viene disegnato al centro.
android:measureAllChildren — un attributo booleano (true per impostazione predefinita) che determina se misurare tutti gli elementi figli quando si calcola la dimensione di FrameLayout. Se true (predefinito), FrameLayout considera le dimensioni di tutti i figli, inclusi GONE (con dimensione 0). Se false, FrameLayout misura solo i figli VISIBLE e INVISIBLE — gli elementi GONE vengono esclusi dal calcolo. Secondo Google I/O 2019, disabilitare measureAllChildren per contenitori con molti elementi GONE (ad esempio, elenchi con visibilità alternata) accelera il disegno iniziale del 20–60%.
android:layout_gravity — un attributo di un elemento figlio di FrameLayout (e di altri ViewGroup) che definisce la sua posizione all'interno del contenitore. In FrameLayout, layout_gravity è l'unico modo per controllare la posizione di un elemento figlio, poiché FrameLayout non fornisce proprie regole di posizionamento (come RelativeLayout) o direzione (come LinearLayout).
Valori possibili: top, bottom, left, right, center, center_horizontal, center_vertical, fill, fill_horizontal, fill_vertical, clip_horizontal, clip_vertical. Combinati tramite |: android:layout_gravity="bottom|center_horizontal" — l'elemento è fissato al bordo inferiore e centrato orizzontalmente. Per elementi più piccoli di FrameLayout, layout_gravity determina la loro posizione nello spazio libero.
Se layout_gravity non è impostato, l'elemento viene posizionato nell'angolo superiore sinistro (top|left) per impostazione predefinita. Per un FrameLayout contenente più figli, ciascuno può avere il proprio layout_gravity — un elemento può essere nell'angolo superiore sinistro, un altro nell'angolo inferiore destro, un terzo centrato. Ciò consente di creare semplici sovrapposizioni (ad esempio, un'icona di chiusura su un'immagine).
android:gravity (attributo genitore) allinea il contenuto all'interno di FrameLayout — ad esempio, il testo all'interno di un TextView. android:layout_gravity (attributo figlio) allinea l'elemento stesso all'interno di FrameLayout. Nel contesto di FrameLayout, gravity definisce come i figli vengono posizionati per impostazione predefinita (simile a layout_gravity per tutti i figli contemporaneamente), ma il layout_gravity di ogni singolo elemento sovrascrive il valore genitore.
Un'immagine con un'etichetta di testo nell'angolo inferiore destro. FrameLayout contiene un ImageView che riempie lo schermo e un TextView con layout_gravity="bottom|end" per il posizionamento sull'immagine.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/product_photo"
android:scaleType="centerCrop" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="8dp"
android:background="@drawable/badge_background"
android:elevation="2dp"
android:paddingHorizontal="8dp"
android:paddingVertical="4dp"
android:text="-30%"
android:textColor="@android:color/white"
android:textSize="14sp"
android:textStyle="bold" />
</FrameLayout>
ImageView riempie l'intero FrameLayout (altezza 200dp). Il TextView con layout_gravity="bottom|end" viene posizionato nell'angolo inferiore destro sopra l'immagine. elevation=2dp aggiunge un'ombra sotto l'etichetta, separandola visivamente dall'immagine. Questo esempio minimo avrebbe richiesto un contenitore annidato o codice personalizzato in LinearLayout.
Uno schermo con contenuto e una barra di progresso centrata che appare durante il caricamento. FrameLayout contiene due elementi: il contenuto e un ProgressBar con visibility="gone" (alternato nel codice).
<FrameLayout
android:id="@+id/content_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/content_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Contenuto caricato"
android:textSize="18sp" />
<ProgressBar
android:id="@+id/loading_spinner"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:visibility="gone" />
</FrameLayout>
La ProgressBar è nascosta per impostazione predefinita (gone). All'avvio del caricamento, viene chiamato findViewById(R.id.loading_spinner).visibility = View.VISIBLE — lo spinner appare centrato sopra il contenuto. Dopo il caricamento — .visibility = View.GONE. FrameLayout fornisce sovrapposizione senza spostare la posizione del contenuto — il testo non si sposta quando appare lo spinner, poiché la ProgressBar viene disegnata sopra.
FrameLayout come contenitore standard per FragmentTransaction. L'attività sostituisce i frammenti all'interno di questo contenitore in base alla navigazione.
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
import androidx.fragment.app.FragmentTransaction
val fragmentContainer = R.id.fragment_container
fun navigateTo(fragment: Fragment) {
supportFragmentManager
.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.replace(fragmentContainer, fragment)
.addToBackStack(null)
.commit()
}
FrameLayout come contenitore di frammenti è il modo più leggero per supportare la navigazione tramite Fragment. FragmentContainerView (una sottoclasse di FrameLayout) è raccomandato con Navigation Component 2.4+, ma un semplice FrameLayout rimane valido per FragmentTransaction manuali. Il vantaggio principale è che FragmentTransaction.replace() sostituisce completamente il contenuto senza influenzare l'Activity.
FrameLayout è ottimale per quattro scenari: contenitore di frammenti (FragmentContainerView o FrameLayout), sovrapposizione di caricamento (ProgressBar sul contenuto), etichette su schede (etichetta su immagine), contenitore segnaposto per ViewStub (caricamento lazy).
Non adatto per: posizionamento complesso di più elementi (usa ConstraintLayout), elenchi sequenziali (LinearLayout o RecyclerView), layout dinamici con modifica dell'ordine degli elementi.
FrameLayout come elemento radice dello schermo — cattiva pratica se lo schermo contiene più di 2–3 elementi. Per un contenitore radice, usa ConstraintLayout — offre più controllo sul posizionamento senza perdita di prestazioni. Usa FrameLayout solo per blocchi figli dove è necessaria sovrapposizione o segnaposto.
Domande frequenti
FrameLayout — un contenitore minimalista senza sistema di posizionamento (solo layout_gravity), progettato per la sovrapposizione di elementi. ConstraintLayout — un sistema di layout completo con constraint, catene, barriere, dimensioni percentuali e supporto di MotionLayout. FrameLayout è più veloce per casi semplici (1 figlio o sovrapposizione); ConstraintLayout è necessario per layout complessi. Scelta: FrameLayout per sovrapposizione di caricamento e contenitore di frammenti; ConstraintLayout per tutto il resto.
Imposta la dimensione di FrameLayout su wrap_content sull'asse desiderato (android:layout_width="wrap_content" e/o android:layout_height="wrap_content"). FrameLayout si adatterà quindi alla dimensione dell'elemento figlio più grande. Se tutti i figli sono più piccoli di FrameLayout, lo spazio in eccesso rimane vuoto. Per un controllo preciso, usa match_parent con padding fisso.
Questo è il comportamento previsto di FrameLayout — non distribuisce i figli nello spazio ma li disegna sequenzialmente uno sopra l'altro. Se vuoi che gli elementi non si sovrappongano, usa un altro contenitore (LinearLayout, ConstraintLayout). Per sovrapposizione parziale con controllo dell'ordine di disegno, usa layout_gravity per l'offset ed elevation per l'ordine Z.
FrameLayout è più veloce per i casi più semplici — un elemento figlio, senza posizionamento complesso. FrameLayout esegue onMeasure in un tempo minimo senza calcolare constraint. Tuttavia, la differenza è evidente solo a centinaia di ripetizioni (RecyclerView con decine di migliaia di elementi). Per uno schermo tipico (1–3 FrameLayout), la differenza è in microsecondi e non influisce sull'UX. ConstraintLayout è la scelta universale per il 95% delle attività.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche