Guida completa a Fragment e FragmentManager in Android (Java)

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
FragmentManagereFragmentTransaction.
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: usarenew DetailFragment(title, id).
Soluzione: usare semprenewInstance()conBundleesetArguments(). In questo modo Android può ricreare correttamente il Fragment dopo cambi di configurazione. - Accedere alle View prima di onCreateView()/onViewCreated()
Errore: usarefindViewById()nel costruttore o inonCreate().
Soluzione: inizializza le View solo inonCreateView()oonViewCreated(). - Conservare riferimenti alla View oltre onDestroyView()
Errore: mantenere campi che referenziano View senza azzerarli inonDestroyView().
Soluzione: imposta i riferimenti UI anullinonDestroyView()per evitare memory leak. - Non usare addToBackStack() quando serve navigazione indietro
Errore: sostituire Fragment senzaaddToBackStack()e aspettarsi che il tasto back torni al Fragment precedente.
Soluzione: chiamaaddToBackStack()per le transazioni che vuoi rendere “annullabili”. - Usare getActivity() senza controllare null
Errore: chiamaregetActivity()e usarlo direttamente, rischiandoNullPointerExceptionse il Fragment è scollegato.
Soluzione: controlla sempre segetActivity() != nulloppure usarequireActivity()se sei sicuro che non sia null (sarà lanciata un’eccezione se lo è). - Mescolare Fragment della support library e nativi
Errore: usareandroid.app.Fragmentconandroidx.fragment.app.FragmentActivity.
Soluzione: usa coerentemente i Fragment di AndroidX:androidx.fragment.app.FragmenteFragmentManagerottenuto congetSupportFragmentManager().
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:
Rispondi