Fragments in Android con Java: Guida Completa con Esempi Pratici

Guida completa a Fragment e FragmentManager in Android (Java)

Fragments in Android Java - ciclo di vita e architettura

I Fragments in Android con Java sono uno strumento fondamentale per costruire interfacce modulari e flessibili. In altre parole, sono una porzione riutilizzabile dell’interfaccia utente e della logica di un’app Android. Puoi pensarlo come una “mini-activity” che vive all’interno di un’Activity. Inoltre, ogni Fragment ha il proprio layout, il proprio ciclo di vita e può essere aggiunto, rimosso o sostituito dinamicamente all’interno di un contenitore nell’Activity. Per questo motivo, consulta la FragmentManager per una panoramica completa sulla documentazione ufficiale Android sui Fragment.

1. Introduzione: cos’è un Fragment e perché usarlo

I Fragments in Android con Java, o più brevemente Fragment, sono una porzione riutilizzabile dell’interfaccia utente e della logica di un’app Android. Puoi pensarlo come una “mini-activity” che vive all’interno di un’Activity. Ogni Fragment ha il proprio layout, il proprio ciclo di vita e può essere aggiunto, rimosso o sostituito dinamicamente all’interno di un contenitore nell’Activity. Per una panoramica completa, consulta la documentazione ufficiale Android sui Fragment.

Perché usare i Fragment?

  • Interfacce flessibili: su tablet puoi mostrare più Fragment affiancati (es. lista + dettaglio); su smartphone puoi mostrarli in schermate separate.
  • Riutilizzo del codice: lo stesso Fragment può essere usato in Activity diverse.
  • Gestione dinamica dell’UI: puoi aggiungere, sostituire e rimuovere blocchi di interfaccia in modo dinamico, usando il back stack.
  • Separazione delle responsabilità: ogni Fragment gestisce una porzione specifica dell’interfaccia e della logica, rendendo il codice più modulare.

Ciclo di vita dei Fragments in Android

Il ciclo di vita di un Fragment è simile a quello di un’Activity, tuttavia con alcune differenze importanti. I metodi principali sono:

  • onAttach()
  • onCreate()
  • onCreateView()
  • onViewCreated()
  • onStart()
  • onResume()
  • onPause()
  • onStop()
  • onDestroyView()
  • onDestroy()
  • onDetach()

2.1 Schema visivo testuale del ciclo di vita

Ecco una rappresentazione testuale semplificata del ciclo di vita di un Fragment:

onAttach(Activity)   -> il Fragment è collegato all'Activity
    |
onCreate(Bundle)    -> inizializzazione logica (no UI)
    |
onCreateView(...)   -> creazione del layout (inflation della View)
    |
onViewCreated(...)  -> la View è stata creata, inizializza componenti UI
    |
onStart()          -> il Fragment è visibile all'utente
    |
onResume()         -> il Fragment è in primo piano e interattivo
    |
   [Fragment in esecuzione]
    |
onPause()         -> il Fragment sta per andare in pausa
    |
onStop()          -> il Fragment non è più visibile
    |
onDestroyView()   -> la View viene distrutta (rilascia riferimenti UI)
    |
onDestroy()       -> pulizia finale delle risorse non legate alla View
    |
onDetach()        -> il Fragment è scollegato dall'Activity

2.2 I metodi principali spiegati

  • onCreate(Bundle savedInstanceState): chiamato per inizializzare la logica del Fragment. Non hai ancora accesso alle View.
  • onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState): qui crei (“infli”) il layout del Fragment e restituisci la View radice.
  • onViewCreated(View view, Bundle savedInstanceState): chiamato subito dopo onCreateView(); quindi la View è pronta, di conseguenza qui inizializzi pulsanti, RecyclerView, listener ecc.
  • onStart(): il Fragment sta diventando visibile.
  • onResume(): il Fragment è visibile e interattivo.
  • onPause() / onStop(): il Fragment sta per essere nascosto o non sarà più visibile.
  • onDestroyView(): distruggi/rilascia riferimenti alla UI per evitare memory leak.
  • onDestroy(): pulizia finale di risorse (es. thread, listener globali).

3. Come creare un Fragment in Android con Java

Vediamo un esempio completo di Fragments in Android con Java. In particolare, questo Fragment mostra un testo e un pulsante, e registra i log del ciclo di vita.

3.1 Layout XML del Fragment

<!-- res/layout/fragment_example.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/textViewMessage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Ciao dal Fragment!"
        android:textSize="18sp" />

    <Button
        android:id="@+id/buttonClickMe"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Cliccami" />

</LinearLayout>

3.2 Classe Java del Fragment

// ExampleFragment.java

import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class ExampleFragment extends Fragment {

    private static final String TAG = "ExampleFragment";

    private TextView textViewMessage;
    private Button buttonClickMe;

    public ExampleFragment() {
        // Costruttore vuoto richiesto
    }

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        Log.d(TAG, "onAttach");
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView");
        // Infla il layout del Fragment
        View view = inflater.inflate(R.layout.fragment_example, container, false);
        return view;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Log.d(TAG, "onViewCreated");

        textViewMessage = view.findViewById(R.id.textViewMessage);
        buttonClickMe = view.findViewById(R.id.buttonClickMe);

        buttonClickMe.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                textViewMessage.setText("Bottone cliccato!");
            }
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.d(TAG, "onStart");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.d(TAG, "onResume");
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.d(TAG, "onPause");
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.d(TAG, "onStop");
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.d(TAG, "onDestroyView");
        // Rilascia riferimenti alla UI se necessario
        textViewMessage = null;
        buttonClickMe = null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }

    @Override
    public void onDetach() {
        super.onDetach();
        Log.d(TAG, "onDetach");
    }
}



4. Aggiungere un Fragment Android a un’Activity (XML e Java)

In pratica, esistono due modi principali per aggiungere un Fragment a un’Activity:

  • tramite XML, dichiarandolo nel layout dell’Activity;
  • tramite codice Java, usando FragmentManager e FragmentTransaction.

4.1 Aggiungere un Fragment tramite XML

Nel layout dell’Activity, ad esempio, puoi usare il tag <fragment> (per Fragment statici) o un contenitore (es. <FrameLayout>) dove inserire dinamicamente il Fragment.

<!-- res/layout/activity_main.xml -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/fragment_container_view"
        android:name="com.example.app.ExampleFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>

In questo modo, il Fragment viene creato automaticamente dal sistema quando l’Activity viene creata.

4.2 Aggiungere un Fragment tramite codice Java

In alternativa, più comune e flessibile è usare un contenitore vuoto (ad esempio un FrameLayout) e inserire il Fragment in modo dinamico.

<!-- res/layout/activity_main.xml -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
// MainActivity.java

import android.os.Bundle;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState == null) {
            // Aggiungi il Fragment solo la prima volta
            ExampleFragment fragment = new ExampleFragment();

            FragmentManager fragmentManager = getSupportFragmentManager();
            FragmentTransaction transaction = fragmentManager.beginTransaction();

            transaction.add(R.id.fragment_container, fragment, "EXAMPLE_FRAGMENT");
            transaction.commit();
        }
    }
}



5. FragmentManager e FragmentTransaction

Nello specifico, FragmentManager gestisce la collezione di Fragment associati a un’Activity. Per modificare i Fragment (aggiungerli, sostituirli, rimuoverli) usi un FragmentTransaction.

5.1 Operazioni principali: add, replace, remove

FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();

Fragment fragmentA = new FragmentA();
Fragment fragmentB = new FragmentB();

// Aggiungere un Fragment
transaction.add(R.id.fragment_container, fragmentA, "FRAGMENT_A");

// Sostituire un Fragment esistente con un altro
transaction.replace(R.id.fragment_container, fragmentB, "FRAGMENT_B");

// Rimuovere un Fragment (se già aggiunto)
transaction.remove(fragmentA);

// Confermare le operazioni
transaction.commit();

5.2 Gestire il back stack con addToBackStack()

A tal fine, per permettere all’utente di tornare al Fragment precedente premendo il tasto back, devi aggiungere la transazione al back stack:

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

transaction.replace(R.id.fragment_container, new FragmentB(), "FRAGMENT_B");
transaction.addToBackStack(null); // oppure un nome, es. "toFragmentB"
transaction.commit();

In questo modo, quando l’utente preme il tasto back, la transazione più recente nel back stack viene annullata, ripristinando il Fragment precedente.

6. Comunicazione tra Fragment e Activity (callback)

Pertanto, la buona pratica è che un Fragment non interagisca direttamente con un’altra Activity o Fragment, ma comunichi con l’Activity che lo ospita tramite una interfaccia callback. L’Activity implementa l’interfaccia e il Fragment la utilizza per inviare eventi.

6.1 Definire l’interfaccia nel Fragment

public class ExampleFragment extends Fragment {

    public interface OnExampleFragmentInteractionListener {
        void onButtonClicked(String message);
    }

    private OnExampleFragmentInteractionListener mListener;

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        if (context instanceof OnExampleFragmentInteractionListener) {
            mListener = (OnExampleFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " deve implementare OnExampleFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    // Esempio di utilizzo (dentro onViewCreated)
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        Button buttonClickMe = view.findViewById(R.id.buttonClickMe);
        buttonClickMe.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mListener != null) {
                    mListener.onButtonClicked("Messaggio dal Fragment");
                }
            }
        });
    }
}



6.2 Implementare l’interfaccia nell’Activity

public class MainActivity extends AppCompatActivity
        implements ExampleFragment.OnExampleFragmentInteractionListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.fragment_container, new ExampleFragment(), "EXAMPLE_FRAGMENT")
                    .commit();
        }
    }

    @Override
    public void onButtonClicked(String message) {
        // Gestisci l'evento proveniente dal Fragment
        Toast.makeText(this, "Ricevuto: " + message, Toast.LENGTH_SHORT).show();
    }
}



7. Passare dati a un Fragment con Bundle e setArguments()

In particolare, il modo consigliato per passare parametri a un Fragment è usare un Bundle tramite il metodo setArguments(), di solito incapsulato in un factory method statico newInstance().

public class DetailFragment extends Fragment {

    private static final String ARG_TITLE = "arg_title";
    private static final String ARG_ID = "arg_id";

    private String title;
    private int id;

    public static DetailFragment newInstance(String title, int id) {
        DetailFragment fragment = new DetailFragment();
        Bundle args = new Bundle();
        args.putString(ARG_TITLE, title);
        args.putInt(ARG_ID, id);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            title = getArguments().getString(ARG_TITLE);
            id = getArguments().getInt(ARG_ID);
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_detail, container, false);
        TextView textViewTitle = view.findViewById(R.id.textViewTitle);
        textViewTitle.setText(title + " (#" + id + ")");
        return view;
    }
}



Infine, per creare il Fragment e passargli i dati:

DetailFragment fragment = DetailFragment.newInstance("Articolo", 42);

getSupportFragmentManager().beginTransaction()
        .replace(R.id.fragment_container, fragment)
        .addToBackStack(null)
        .commit();

8. Esempio pratico completo di Fragments in Android con Java: due Fragment che comunicano

A titolo di esempio pratico, realizziamo un’app semplice con:

  • InputFragment: contiene un campo di testo e un pulsante. Quando l’utente preme il pulsante, il testo inserito viene inviato all’Activity.
  • DisplayFragment: mostra il testo ricevuto dall’Activity.

In questo schema, l’Activity fa da mediatore tra i due Fragment.

8.1 Layout dell’Activity con due contenitori

<!-- res/layout/activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <FrameLayout
        android:id="@+id/container_input"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <FrameLayout
        android:id="@+id/container_display"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

</LinearLayout>

8.2 InputFragment

<!-- res/layout/fragment_input.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <EditText
        android:id="@+id/editTextMessage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Inserisci un messaggio" />

    <Button
        android:id="@+id/buttonSend"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Invia" />

</LinearLayout>
// InputFragment.java

import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class InputFragment extends Fragment {

    public interface OnInputListener {
        void onInputSent(String text);
    }

    private OnInputListener mListener;

    private EditText editTextMessage;
    private Button buttonSend;

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        if (context instanceof OnInputListener) {
            mListener = (OnInputListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " deve implementare InputFragment.OnInputListener");
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_input, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        editTextMessage = view.findViewById(R.id.editTextMessage);
        buttonSend = view.findViewById(R.id.buttonSend);

        buttonSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String text = editTextMessage.getText().toString();
                if (!TextUtils.isEmpty(text) && mListener != null) {
                    mListener.onInputSent(text);
                }
            }
        });
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }
}



8.3 DisplayFragment

<!-- res/layout/fragment_display.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textViewDisplay"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="In attesa di messaggio..."
        android:textSize="20sp" />

</LinearLayout>
// DisplayFragment.java

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class DisplayFragment extends Fragment {

    private TextView textViewDisplay;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_display, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        textViewDisplay = view.findViewById(R.id.textViewDisplay);
    }

    public void updateText(String text) {
        if (textViewDisplay != null) {
            textViewDisplay.setText(text);
        }
    }
}



8.4 Activity che collega i due Fragment

// MainActivity.java

import android.os.Bundle;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;

public class MainActivity extends AppCompatActivity implements InputFragment.OnInputListener {

    private DisplayFragment displayFragment;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        FragmentManager fragmentManager = getSupportFragmentManager();

        if (savedInstanceState == null) {
            InputFragment inputFragment = new InputFragment();
            displayFragment = new DisplayFragment();

            fragmentManager.beginTransaction()
                    .add(R.id.container_input, inputFragment, "INPUT_FRAGMENT")
                    .add(R.id.container_display, displayFragment, "DISPLAY_FRAGMENT")
                    .commit();
        } else {
            // Recupera il riferimento al DisplayFragment già esistente
            displayFragment = (DisplayFragment) fragmentManager
                    .findFragmentByTag("DISPLAY_FRAGMENT");
        }
    }

    @Override
    public void onInputSent(String text) {
        if (displayFragment != null) {
            displayFragment.updateText(text);
        }
    }
}



Errori comuni con i Fragments in Android con Java

  • Creare il Fragment con il costruttore parametrizzato
    Errore: usare new DetailFragment(title, id).
    Soluzione: usare sempre newInstance() con Bundle e setArguments(). In questo modo Android può ricreare correttamente il Fragment dopo cambi di configurazione.
  • Accedere alle View prima di onCreateView()/onViewCreated()
    Errore: usare findViewById() nel costruttore o in onCreate().
    Soluzione: inizializza le View solo in onCreateView() o onViewCreated().
  • Conservare riferimenti alla View oltre onDestroyView()
    Errore: mantenere campi che referenziano View senza azzerarli in onDestroyView().
    Soluzione: imposta i riferimenti UI a null in onDestroyView() per evitare memory leak.
  • Non usare addToBackStack() quando serve navigazione indietro
    Errore: sostituire Fragment senza addToBackStack() e aspettarsi che il tasto back torni al Fragment precedente.
    Soluzione: chiama addToBackStack() per le transazioni che vuoi rendere “annullabili”.
  • Usare getActivity() senza controllare null
    Errore: chiamare getActivity() e usarlo direttamente, rischiando NullPointerException se il Fragment è scollegato.
    Soluzione: controlla sempre se getActivity() != null oppure usa requireActivity() se sei sicuro che non sia null (sarà lanciata un’eccezione se lo è).
  • Mescolare Fragment della support library e nativi
    Errore: usare android.app.Fragment con androidx.fragment.app.FragmentActivity.
    Soluzione: usa coerentemente i Fragment di AndroidX: androidx.fragment.app.Fragment e FragmentManager ottenuto con getSupportFragmentManager().

Conclusione: Fragments in Android con Java

In conclusione, i Fragments in Android con Java sono uno strumento fondamentale per costruire interfacce Android modulari, riutilizzabili e adattabili a diverse configurazioni di schermo. Inoltre, imparare a gestire correttamente il loro ciclo di vita, il FragmentManager, le transazioni e la comunicazione con le Activity ti permetterà di realizzare architetture più pulite e scalabili.

Per completare il percorso, puoi approfondire argomenti spesso usati insieme ai Fragment consultando le seguenti guide:

Commenti

Rispondi

Scopri di più da App Tutorial

Abbonati ora per continuare a leggere e avere accesso all'archivio completo.

Continua a leggere