Guida completa a FrameLayout in Android (con esempi Java)
In questa guida approfondita analizziamo il FrameLayout di Android: cos'è, quando usarlo, come gestire la sovrapposizione delle viste, l'attributo layout_gravity, l'aggiunta dinamica di View da Java, la gestione dei click e un esempio completo di schermata. Questa guida è specifica per FrameLayout e non sostituisce né sovrappone la guida generale ai layout XML in Android, né quella dedicata a libGDX / OpenGL ES.
Che cos'è FrameLayout in Android
FrameLayout è un tipo di ViewGroup (contenitore) molto semplice che permette di posizionare una o più View una sopra l'altra, occupando in genere tutto lo spazio disponibile. A differenza di LinearLayout o ConstraintLayout, non gestisce posizionamenti complessi: il suo scopo principale è fungere da "cornice" (frame) per un singolo elemento o per viste sovrapposte.
Caratteristiche principali:
- È un contenitore leggero: poche regole di layout, sovraccarico minimo.
- Per impostazione predefinita mostra solo la vista più in alto (l'ultima aggiunta) quando occupano la stessa area.
- Supporta la sovrapposizione di elementi (ad es. testo sopra un'immagine, pulsante flottante, ecc.).
- È spesso usato come contenitore di placeholder per inserire frammenti o contenuti dinamici.
Questa guida si concentra su XML e Java del framework Android standard. Non tratteremo qui layout complessi multi-pannello (coperti dalla guida generale ai layout) né l'uso di FrameBuffer o viewport grafici tipici di libGDX/OpenGL ES.
Quando usare FrameLayout
Alcuni scenari tipici in cui FrameLayout è la scelta migliore:
- Contenitore per Fragments: ad esempio
<FrameLayout android:id="@+id/fragment_container" ... />per ospitare uno o più fragment. - Overlay e sovrapposizioni: mostrare un
ProgressBarsopra il contenuto, una barra di caricamento, un pannello semi-trasparente. - Immagine con testo/pulsante sopra: banner, card promozionali, hero con call-to-action.
- Placeholder dinamici: quando il contenuto preciso non è noto a priori e viene aggiunto da Java.
- Container semplice per una singola View che deve occupare tutto lo spazio disponibile (più leggero di layout più complessi).
Quando non usare FrameLayout:
- Layout in colonna o riga (meglio
LinearLayoutoConstraintLayout). - Layout complessi con vincoli multipli (meglio
ConstraintLayout). - Liste o griglie scrollabili (meglio
RecyclerView,ListView,GridLayout, ecc.).
Sintassi base di FrameLayout in XML
Ecco un esempio minimale di FrameLayout con una singola TextView:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text_hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Ciao da FrameLayout"/>
</FrameLayout>
Di default, il TextView sarà posizionato in alto a sinistra. Per centrarlo, useremo layout_gravity, come vedremo più avanti.
Come sovrapporre più View in un FrameLayout
Per sovrapporre viste, basta dichiararle una dopo l'altra dentro il FrameLayout. L'ordine è importante: l'ultima View dichiarata è quella più in alto.
Esempio: immagine di sfondo con testo e bottone sovrapposti al centro.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="200dp">
<ImageView
android:id="@+id/image_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/sample_banner"/>
<TextView
android:id="@+id/text_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|center_vertical"
android:text="Promo speciale"
android:textColor="@android:color/white"
android:textSize="20sp"/>
<Button
android:id="@+id/button_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="16dp"
android:text="Scopri di più"/>
</FrameLayout>
In questo esempio:
- L'
ImageViewoccupa tutto il contenitore ed è il livello di sfondo. - Il
TextViewè centrato grazie alayout_gravity. - Il
Buttonè allineato in basso al centro, con un margine dal bordo inferiore.
Usare layout_gravity in FrameLayout
android:layout_gravity è l'attributo chiave per controllare la posizione di una View dentro un FrameLayout. A differenza di gravity (che allinea il contenuto interno di una View), layout_gravity allinea l'intera View nel suo contenitore.
Valori comuni:
top,bottom,left,rightcenter_vertical,center_horizontal,center- Combinazioni con barra verticale, ad es.
bottom|center_horizontal
Esempio di tre elementi posizionati diversamente:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|left"
android:text="In alto a sinistra"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Centrato"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:text="In basso a destra"
android:layout_margin="8dp"/>
</FrameLayout>
Ricorda che se non specifichi layout_gravity, la View sarà posizionata nell'angolo in alto a sinistra per impostazione predefinita.
Aggiungere dinamicamente View a un FrameLayout da Java
Uno dei casi d'uso più comuni è aggiungere o rimuovere View a runtime. Supponiamo di avere questo layout XML:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/frame_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
In Java possiamo creare e aggiungere dinamicamente una TextView e un Button:
public class MainActivity extends AppCompatActivity {
private FrameLayout frameContainer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
frameContainer = findViewById(R.id.frame_container);
// Aggiungi dinamicamente un TextView centrato
TextView dynamicText = new TextView(this);
dynamicText.setText("Testo aggiunto da Java");
dynamicText.setTextSize(18f);
FrameLayout.LayoutParams textParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
);
textParams.gravity = Gravity.CENTER; // equivale a layout_gravity="center"
dynamicText.setLayoutParams(textParams);
frameContainer.addView(dynamicText);
// Aggiungi dinamicamente un Button in basso a destra
Button dynamicButton = new Button(this);
dynamicButton.setText("Chiudi");
FrameLayout.LayoutParams buttonParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
);
buttonParams.gravity = Gravity.BOTTOM | Gravity.RIGHT;
buttonParams.setMargins(16, 16, 16, 16);
dynamicButton.setLayoutParams(buttonParams);
frameContainer.addView(dynamicButton);
}
}
Nota: in Java usiamo FrameLayout.LayoutParams e la proprietà gravity per ottenere lo stesso effetto di android:layout_gravity in XML.
Aggiungere ImageView, TextView e Button con gestione dei click
Vediamo ora un esempio Java più strutturato, che aggiunge tre tipi di View e gestisce i loro eventi di click.
Layout XML (contenitore vuoto):
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/frame_root"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
Codice Java:
public class OverlayActivity extends AppCompatActivity {
private FrameLayout frameRoot;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_overlay);
frameRoot = findViewById(R.id.frame_root);
// 1. ImageView a pieno schermo (sfondo)
ImageView bgImage = new ImageView(this);
bgImage.setImageResource(R.drawable.sample_background);
bgImage.setScaleType(ImageView.ScaleType.CENTER_CROP);
FrameLayout.LayoutParams bgParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
);
bgImage.setLayoutParams(bgParams);
frameRoot.addView(bgImage);
// 2. TextView centrato
TextView title = new TextView(this);
title.setText("Titolo sopra immagine");
title.setTextColor(Color.WHITE);
title.setTextSize(22f);
title.setPadding(16, 16, 16, 16);
FrameLayout.LayoutParams titleParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
);
titleParams.gravity = Gravity.CENTER;
title.setLayoutParams(titleParams);
frameRoot.addView(title);
// 3. Button in basso al centro
Button actionButton = new Button(this);
actionButton.setText("Premi qui");
FrameLayout.LayoutParams buttonParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
);
buttonParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
buttonParams.setMargins(16, 16, 16, 48);
actionButton.setLayoutParams(buttonParams);
frameRoot.addView(actionButton);
// Gestione click
bgImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(OverlayActivity.this,
"Hai cliccato sullo sfondo", Toast.LENGTH_SHORT).show();
}
});
title.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(OverlayActivity.this,
"Hai cliccato sul titolo", Toast.LENGTH_SHORT).show();
}
});
actionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(OverlayActivity.this,
"Bottone premuto", Toast.LENGTH_SHORT).show();
}
});
}
}
L'ordine delle chiamate addView() determina quali elementi stanno sopra: in questo esempio, il bottone è sopra il testo, e il testo è sopra l'immagine.
Esempio completo: schermata con overlay di caricamento
Realizziamo ora un esempio pratico spesso usato in app reali: una schermata con contenuto principale e un overlay di caricamento (spinner) mostrato/ nascosto a runtime.
Layout XML
In activity_loading.xml usiamo un FrameLayout come root e al suo interno un layout di contenuto (qui un semplice LinearLayout) e un overlay con ProgressBar:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/frame_root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Contenuto principale -->
<LinearLayout
android:id="@+id/content_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/text_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Dati non ancora caricati"
android:textSize="18sp"/>
<Button
android:id="@+id/button_load"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Carica dati"
android:layout_marginTop="16dp"/>
</LinearLayout>
<!-- Overlay di caricamento -->
<LinearLayout
android:id="@+id/loading_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:background="#80000000" <!-- nero semi-trasparente -->
android:visibility="gone">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Caricamento..."
android:textColor="@android:color/white"
android:layout_marginTop="8dp"/>
</LinearLayout>
</FrameLayout>
Notare che:
- Il layout di contenuto viene dichiarato prima.
- L'overlay di caricamento viene dichiarato dopo, quindi sarà disegnato sopra.
- L'overlay occupa tutto lo schermo e usa uno sfondo semi-trasparente (RGBA con alpha al 50%).
Codice Java dell'Activity
public class LoadingActivity extends AppCompatActivity {
private LinearLayout loadingOverlay;
private TextView textData;
private Button buttonLoad;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loading);
loadingOverlay = findViewById(R.id.loading_overlay);
textData = findViewById(R.id.text_data);
buttonLoad = findViewById(R.id.button_load);
buttonLoad.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startFakeLoading();
}
});
}
private void startFakeLoading() {
// Mostra overlay
loadingOverlay.setVisibility(View.VISIBLE);
textData.setText("Caricamento in corso...");
buttonLoad.setEnabled(false);
// Simula un caricamento asincrono (es. chiamata rete)
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
// Nascondi overlay e aggiorna dati
loadingOverlay.setVisibility(View.GONE);
textData.setText("Dati caricati con successo");
buttonLoad.setEnabled(true);
}
}, 3000); // 3 secondi di "caricamento"
}
}
Qui il FrameLayout rende semplice la gestione del layer di caricamento sopra il contenuto principale, senza ricorrere a layout più complessi.
Buone pratiche nell'uso di FrameLayout
- Usalo come contenitore semplice: se devi allineare molti elementi in modo complesso, è meglio un layout più espressivo.
- Mantieni pochi child: con molti elementi sovrapposti, il layout può diventare difficile da mantenere.
- Nomina chiaramente gli ID: ad esempio
frame_container,overlay_loading,frame_fragment_host. - Gestisci bene la visibilità degli overlay (usa
View.GONEquando possibile, per evitare di intercettare click sotto di essi). - Centralizza la logica di overlay (metodi
showOverlay(),hideOverlay()) anziché manipolare la visibilità in molti punti. - Distingui i casi d'uso: per layout generali studia la guida ai layout XML; per rendering grafico avanzato, segui la guida su libGDX/OpenGL ES.
Errori comuni da evitare
- Dimenticare l'ordine delle View: aggiungere l'overlay prima del contenuto e chiedersi perché non si vede (o viceversa).
- Non usare layout_gravity: posizionare manualmente con margini enormi invece di allineare con
center,bottom|center_horizontal, ecc. - Overlay sempre visibile: impostare
visibility="visible"per un overlay che dovrebbe essere mostrato solo durante il caricamento. - Ignorare il tocco pass-through: overlay visibili ma trasparenti che intercettano comunque i click (usa
GONEo gestisci correttamenteclickableefocusablese necessario). - Usare FrameLayout per layout complessi dove sarebbe più leggibile un
ConstraintLayouto una combinazione di layout.
Conclusione
FrameLayout è un componente fondamentale del toolkit Android, leggero e potentissimo per tutti i casi in cui devi sovrapporre viste o mostrare contenuti dinamici (come fragment o overlay di caricamento). A differenza delle guide generali ai layout XML, qui abbiamo visto come usarlo in modo mirato: dalla sintassi base in XML agli esempi Java con aggiunta dinamica di ImageView, TextView e Button, fino a una schermata completa con overlay di caricamento e gestione degli eventi di click. Usandolo con criterio e conoscendo i suoi limiti, puoi semplificare sensibilmente molte interfacce dell'app mantenendo il codice più pulito e manutenibile.