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 ProgressBar sopra 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 LinearLayout o ConstraintLayout).
  • 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&ugrave;"/>

</FrameLayout>

In questo esempio:

  • L'ImageView occupa tutto il contenitore ed è il livello di sfondo.
  • Il TextView è centrato grazie a layout_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, right
  • center_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.GONE quando 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 GONE o gestisci correttamente clickable e focusable se necessario).
  • Usare FrameLayout per layout complessi dove sarebbe più leggibile un ConstraintLayout o 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.