Fragments (Java)
A Fragment is a reusable portion of UI + behavior that you place inside an Activity (or another Fragment’s view). Fragments have their own lifecycle, participate in the Activity’s back stack, and make multi-pane/tablet UIs easier. Think of them as screens inside a screen.
When to Use Fragments
Reusable UI
Same card/list used in multiple Activities
Adaptive Layouts
Phone (single pane) vs Tablet (two-pane)
Dialog UI
Use DialogFragment
Nav Host
Navigation Component destination
Activity vs Fragment (At a Glance)
| Aspect | Activity | Fragment |
|---|---|---|
| Purpose | Top-level screen | Reusable UI block inside Activity |
| Lifecycle Owner | Yes | Yes (getViewLifecycleOwner() for Views) |
| Navigation | startActivity / NavHost | FragmentTransaction / NavController |
| Back Stack | System back stack | FragmentManager back stack |
| UI Container | Window DecorView | FragmentContainerView / any container |
Create a Fragment (Java)
// HelloFragment.java
public class HelloFragment extends Fragment {
public static HelloFragment newInstance(String name) {
HelloFragment f = new HelloFragment();
Bundle args = new Bundle();
args.putString("name", name);
f.setArguments(args);
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_hello, container, false);
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
String name = getArguments() != null ? getArguments().getString("name") : "Guest";
TextView tv = view.findViewById(R.id.tvHello);
tv.setText("Hello, " + name);
}
@Override
public void onDestroyView() {
super.onDestroyView();
// null out view references or viewBinding objects here
}
}
Layout
<!-- res/layout/fragment_hello.xml -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<TextView
android:id="@+id/tvHello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello Fragment"
android:textSize="22sp" />
</FrameLayout>
Adding Fragments to an Activity
1) In XML with FragmentContainerView
<!-- res/layout/activity_host.xml -->
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
2) Dynamically via FragmentTransaction
// In HostActivity.java
Fragment f = HelloFragment.newInstance("Sonu");
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, f) // or add()
.addToBackStack(null) // optional: back navigates previous fragment
.commit();
Back Stack & Transactions
| API | Effect | Notes |
|---|---|---|
| addToBackStack(name) | Push transaction on stack | Back button will reverse it |
| commit() | Asynchronous | Safe for UI-thread usage |
| commitNow() | Synchronous | Do only when needed; must be on main thread |
| commitAllowingStateLoss() | May lose state | Last resort (e.g., during onStop); avoid if possible |
Communication Patterns
1) Activity ↔ Fragment using interface (classic)
// In Fragment
public interface OnHelloClick {
void onHello(String name);
}
private OnHelloClick callback;
@Override public void onAttach(@NonNull Context ctx) {
super.onAttach(ctx);
if (ctx instanceof OnHelloClick) callback = (OnHelloClick) ctx;
}
private void notifyHello(){ if(callback!=null) callback.onHello("Sonu"); }
2) Shared ViewModel (recommended for siblings)
// Shared ViewModel scoped to Activity
public class SharedVM extends ViewModel {
public MutableLiveData<String> selected = new MutableLiveData<>();
}
// In both fragments
SharedVM vm = new ViewModelProvider(requireActivity()).get(SharedVM.class);
vm.selected.observe(getViewLifecycleOwner(), value -> { // update UI });
DialogFragment
public class ConfirmDialog extends DialogFragment {
@NonNull @Override
public Dialog onCreateDialog(Bundle s) {
return new AlertDialog.Builder(getActivity())
.setTitle("Confirm")
.setMessage("Proceed?")
.setPositiveButton("Yes", (d,w) -> {/* ... */})
.setNegativeButton("No", null)
.create();
}
}
Child Fragments
Fragments can host other fragments (tabs, pagers). Use getChildFragmentManager() for nested transactions.
RecyclerView inside Fragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle s) {
return inflater.inflate(R.layout.fragment_list, container, false);
}
@Override
public void onViewCreated(@NonNull View v, Bundle s) {
RecyclerView rv = v.findViewById(R.id.recycler);
rv.setLayoutManager(new LinearLayoutManager(getContext()));
rv.setAdapter(new ItemsAdapter());
}
State & Restoration
- Use setArguments(Bundle) for construction params (persisted automatically).
- Put UI state in ViewModel; release view references in onDestroyView().
- Let the system recreate fragments; avoid manual fragment caching in singletons.
Fragment Results (no direct interface)
// Sender (child fragment)
Bundle result = new Bundle();
result.putString("selected", "Item-42");
getParentFragmentManager().setFragmentResult("pick_key", result);
// Receiver (parent or host fragment/activity)
getSupportFragmentManager().setFragmentResultListener("pick_key", this, (req, bundle) -> {
String id = bundle.getString("selected");
// use id
});
FragmentFactory (advanced)
Inject dependencies into fragments via a custom FragmentFactory so the system uses your constructor.
public class MyFactory extends FragmentFactory {
private final Repo repo;
public MyFactory(Repo r){ repo = r; }
@NonNull @Override
public Fragment instantiate(@NonNull ClassLoader cl, @NonNull String className) {
if(className.equals(HelloFragment.class.getName())) {
return new HelloFragment(repo);
}
return super.instantiate(cl, className);
}
}
Troubleshooting
“Can’t perform this action after onSaveInstanceState” ➜ You’re committing after state saved. Move transaction earlier or avoid using commitAllowingStateLoss() unless absolutely necessary.
Leaking Views ➜ Null out view bindings in onDestroyView(), not in onDestroy().
Back button not working ➜ Did you call addToBackStack()? For Nav Component, use NavController.
Nested fragments not showing ➜ Use getChildFragmentManager() instead of host manager.
Best Practices
- Use ViewBinding or findViewById and clear references in onDestroyView().
- Keep Fragments UI-focused; put business logic in ViewModels/repositories.
- Prefer FragmentContainerView over the old <fragment> tag.
- Share data across fragments with a shared ViewModel scoped to the Activity.
- For complex flows, use the Navigation Component (it manages back stack & arguments safely).
Hands-On Exercise
- Create ListFragment (shows items) and DetailFragment (shows details).
- On item click, navigate to DetailFragment with the selected ID in arguments.
- Use a shared ViewModel to update a toolbar title based on the selected item.
- Add transactions to the back stack and verify back navigation works.