Skip to content

Android - Fragments

A Fragment represents a reusable portion of your application’s UI or behavior within an Activity. You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running.

Key characteristics and benefits of using Fragments:

  • Modularity: Fragments allow you to break down your Activity’s UI and logic into self-contained, reusable components.
  • Adaptable UIs: You can combine multiple fragments in a single activity to build a multi-pane UI (e.g., for tablets) and reuse those same fragments individually for single-pane UIs (e.g., for handsets).
  • Lifecycle: A fragment has its own lifecycle, which is closely tied to the lifecycle of its host Activity. When the activity is paused, all fragments within it are also paused, and so on.
  • Reusability: A fragment can be used in multiple activities.
  • Headless Fragments: Fragments don’t necessarily need to have a UI. You can create headless fragments to encapsulate background behavior or data processing tied to an Activity’s lifecycle.
  • Integration with Modern Android Jetpack: Fragments are a core part of Android Jetpack, integrating well with components like ViewModel, LiveData, and the Navigation Component.

You create fragments by extending the androidx.fragment.app.Fragment class. You can then insert a fragment into your activity’s layout either declaratively in the XML layout file using the <fragment> tag or programmatically using FragmentManager and FragmentTransaction.

Imagine an application that displays a list of articles. On a handset, selecting an article might navigate to a new screen (Activity) to show the article’s content. On a tablet, due to more screen real estate, the list of articles could be in one fragment on the left, and the selected article’s content could be displayed in another fragment on the right, all within the same Activity. Fragments make this kind of adaptive design much easier to implement.

A fragment’s lifecycle is directly affected by its host Activity’s lifecycle. For example, when the activity receives onPause(), each fragment in the activity receives onPause().

Here are the key lifecycle callback methods you can override in your Fragment class (from androidx.fragment.app.Fragment):

  • onAttach(Context context): Called when the fragment has been associated with an activity (the Activity is passed in here, though it’s safer to use context). This is called before onCreate().
  • onCreate(Bundle savedInstanceState): The system calls this when creating the fragment. You should initialize essential components of the fragment that you want to retain when the fragment is paused or stopped, then resumed. This is a good place to initialize ViewModels.
  • onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState): The system calls this when it’s time for the fragment to draw its user interface for the first time. You must return a View from this method that is the root of your fragment’s layout. You can return null if the fragment does not provide a UI. This is where you inflate your layout XML.
  • onViewCreated(View view, Bundle savedInstanceState): Called immediately after onCreateView() has returned, but before any saved state has been restored into the view. This is where you should set up your views (e.g., find views by ID, set up listeners, observe LiveData).
  • onStart(): Called when the fragment is visible to the user (its activity has been started).
  • onResume(): Called when the fragment is visible and actively running (its activity has been resumed). The fragment can now interact with the user.
  • onPause(): The system calls this method as the first indication that the user is leaving the fragment (though it does not always mean the fragment is being destroyed). This is usually where you should commit any changes that should be persisted beyond the current user session (e.g., save user input).
  • onStop(): Called when the fragment is no longer visible to the user. This may happen because the activity is being stopped or a new fragment is covering it.
  • onDestroyView(): Called when the view hierarchy associated with the fragment is being removed. This is where you should clean up resources associated with the fragment’s view (e.g., clear references to views to prevent memory leaks).
  • onDestroy(): Called to do final cleanup of the fragment’s state. This is called when the fragment is no longer in use. Not guaranteed to be called if the process is killed.
  • onDetach(): Called when the fragment is being disassociated from the activity. This is the counterpart to onAttach().

Implementing fragments involves a few key steps:

  1. Define the Fragment Class: Create a class that extends androidx.fragment.app.Fragment. Implement necessary lifecycle callbacks, especially onCreateView() to provide the fragment’s layout.
  2. Create the Fragment’s Layout: Design an XML layout file for the fragment’s UI.
  3. Add the Fragment to an Activity: You can do this in two ways:
  4. Manage Fragment Transactions: Use getSupportFragmentManager() (from FragmentActivity or AppCompatActivity) to begin transactions for adding, replacing, or removing fragments.

Let’s create a simple example where an Activity dynamically loads a Fragment into a container.

  1. Create MyFragment.java:
package com.example.fragmentapp;
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 MyFragment extends Fragment {
private static final String ARG_MESSAGE = "message";
public static MyFragment newInstance(String message) {
MyFragment fragment = new MyFragment();
Bundle args = new Bundle();
args.putString(ARG_MESSAGE, message);
fragment.setArguments(args);
return fragment;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_my, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextView textView = view.findViewById(R.id.fragment_textview);
if (getArguments() != null) {
String message = getArguments().getString(ARG_MESSAGE);
textView.setText(message != null ? message : "Hello from MyFragment!");
}
}
}
  1. Create layout res/layout/fragment_my.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp"
android:background="#E0E0E0">
<TextView
android:id="@+id/fragment_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="Hello from MyFragment!" />
</LinearLayout>
  1. Modify MainActivity.java and res/layout/activity_main.xml:

res/layout/activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<Button
android:id="@+id/button_load_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Load Fragment" />
</LinearLayout>

MainActivity.java:

package com.example.fragmentapp;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import android.os.Bundle;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button loadFragmentButton = findViewById(R.id.button_load_fragment);
loadFragmentButton.setOnClickListener(v -> {
loadFragment();
});
// Optionally load fragment initially
if (savedInstanceState == null) { // Ensure fragment is added only once
loadFragmentWithMessage("Initial Fragment Load");
}
}
private void loadFragment() {
loadFragmentWithMessage("Fragment Loaded by Button");
}
private void loadFragmentWithMessage(String message) {
// Create an instance of the fragment
MyFragment myFragment = MyFragment.newInstance(message);
// Get the FragmentManager and start a transaction
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
fragmentTransaction.replace(R.id.fragment_container, myFragment);
fragmentTransaction.addToBackStack(null); // Optional: if you want back navigation
// Commit the transaction
fragmentTransaction.commit();
}
}
  • Use androidx.fragment.app.FragmentContainerView as the container for dynamically added fragments. It offers better handling of fragment animations and z-ordering.
  • Pass arguments to fragments using a Bundle via Fragment.setArguments(Bundle). Use a static newInstance() factory method within the fragment class to encapsulate argument creation.
  • Communicate between fragments and activities using ViewModels and LiveData (or Flow with Kotlin). This decouples components and handles lifecycle complexities.
  • Use the Navigation Component for managing fragment navigation and passing data between destinations. It simplifies complex navigation graphs.
  • For simple dialogs, use DialogFragment.
  • Ensure you are using the androidx.fragment library, not the deprecated platform version.

Fragments are a powerful tool for building flexible and adaptive Android UIs. Mastering their lifecycle and interaction patterns is key to developing robust Android applications.