Fragments

Android Development Fundamentals
Course 1 ยท Chapter 6 ยท Fragments

๐Ÿงฉ Fragments

A Fragment is a reusable, self-contained piece of UI and behavior that lives inside an Activity โ€” closer to a component in web frameworks than anything covered so far in this course. This chapter covers the Fragment lifecycle (which nests inside the Activity lifecycle from Chapter 2), the FragmentManager that adds and swaps Fragments, and the two standard ways a Fragment talks back to its hosting Activity.

๐Ÿ–ผ๏ธ What a Fragment Is, and Why It Exists

An Activity represents a full screen; a Fragment represents a portion of one, with its own layout and its own Kotlin class โ€” designed to be combined, swapped, and reused across different Activities or screen configurations (a tablet showing two Fragments side by side where a phone shows them one at a time, for instance).

Fragments vs Web Components

React ComponentAndroid Fragment
RepresentsA reusable piece of UIA reusable piece of UI
Has its own lifecycle?Yes (mount/update/unmount)Yes, nested inside the host Activity's
Hosted byA parent component / the DOM treeAn Activity (or another Fragment)
Swapped at runtime?Via conditional rendering / routerVia FragmentManager transactions

๐Ÿ” The Fragment Lifecycle

A Fragment has its own set of lifecycle callbacks, layered on top of (and driven by) its host Activity's lifecycle from Chapter 2:

class ProfileFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { return inflater.inflate(R.layout.fragment_profile, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // Safe to touch the Fragment's own Views from here onward Log.d("ProfileFragment", "View is ready") } override fun onDestroyView() { super.onDestroyView() // The Fragment's View is being torn down โ€” clear any View references here } }

onCreateView โ€” Inflate, Don't Configure

Inflates and returns the Fragment's layout โ€” the Fragment equivalent of an Activity's setContentView, except the View is returned rather than passed to a framework method.

onViewCreated โ€” Configure Here Instead

Called right after onCreateView, with the inflated View available as a parameter โ€” the right place to set up click listeners, bind data, and generally do what Chapter 2's Activity onCreate did, but for a Fragment's Views specifically.

โš  A Fragment's View Can Be Destroyed Without the Fragment Itself Being Destroyed

This is the trap that catches most beginners: a Fragment instance can survive (e.g. kept on a back stack) while its View is torn down and later recreated โ€” onDestroyView fires without onDestroy. Holding a View reference in a regular property beyond onDestroyView is a classic memory-leak/crash source; view binding references (Chapter 3) should be nulled out in onDestroyView in real production code โ€” simplified away here to keep the examples focused, but worth knowing exists.

๐Ÿ—‚๏ธ FragmentManager โ€” Adding & Swapping Fragments

An Activity's layout reserves a container (a FrameLayout, typically), and the FragmentManager adds, replaces, or removes Fragments inside it at runtime:

// activity_main.xml โ€” a container for Fragments to live in <FrameLayout android:id="@+id/fragmentContainer" android:layout_width="match_parent" android:layout_height="match_parent" />
// MainActivity.kt โ€” inside onCreate, showing a Fragment for the first time if (savedInstanceState == null) { supportFragmentManager.beginTransaction() .replace(R.id.fragmentContainer, ProfileFragment()) .commit() }

The if (savedInstanceState == null) check matters: without it, rotating the device (Chapter 2) would re-add a duplicate Fragment on top of the one the FragmentManager already automatically restored. replace(...) swaps out whatever Fragment currently occupies that container for a new one โ€” add(...) stacks a new one on top instead, without removing what's there.

โš  This Chapter Shows FragmentManager Directly โ€” Real Apps Usually Don't

Manually calling supportFragmentManager.beginTransaction()... is genuinely how Fragment swapping works underneath, but next chapter's Navigation Component provides a higher-level, declarative way to manage Fragment navigation (a nav graph, back stack handling, and passing arguments all handled for you) that most real apps use instead. Understanding the manual mechanism first is what makes the Navigation Component feel like a natural upgrade rather than unexplained magic.

๐Ÿ“ข Talking Back to the Host Activity

A Fragment shouldn't hold a hard reference to a specific Activity class (that would defeat its whole reusability point) โ€” the standard pattern is an interface the Fragment defines and the hosting Activity implements:

class ProfileFragment : Fragment() { interface ProfileListener { fun onProfileSaved(name: String) } private var listener: ProfileListener? = null override fun onAttach(context: Context) { super.onAttach(context) listener = context as? ProfileListener // safe cast โ€” null if the Activity doesn't implement it } fun saveProfile(name: String) { listener?.onProfileSaved(name) // safe call โ€” no-op if listener is null } } // MainActivity.kt class MainActivity : AppCompatActivity(), ProfileFragment.ProfileListener { override fun onProfileSaved(name: String) { Log.d("MainActivity", "Fragment reported: $name") } }

context as? ProfileListener is a safe cast (Kotlin Fundamentals Chapter 3's ?./?: family, applied to a cast rather than a call) โ€” it yields null instead of crashing if the hosting Activity doesn't implement the interface, which the following listener?.onProfileSaved(...) then handles gracefully by simply doing nothing.

๐Ÿ’ก Shared ViewModel Is the Modern Alternative

The interface-callback pattern above is the traditional approach and worth knowing, but modern Android code increasingly favors a shared ViewModel (scoped to the Activity, accessible from every hosted Fragment) for cross-Fragment communication instead โ€” covered fully once ViewModels are introduced in Course 2. Both solve the same underlying problem; the interface pattern is the more foundational one to understand first.

๐Ÿ’ป Coding Challenges

Challenge 1: A Basic Fragment

Create a WelcomeFragment with a simple layout (one TextView), implementing onCreateView. Add a FrameLayout container to MainActivity's layout, and load WelcomeFragment into it via FragmentManager in onCreate, guarded by the savedInstanceState null check.

Goal: Practice the basic Fragment creation and FragmentManager loading pattern.

โ†’ Solution

Challenge 2: Swapping Fragments

Create a second Fragment, DetailsFragment, and add a Button to WelcomeFragment that, when clicked, uses FragmentManager to replace WelcomeFragment with DetailsFragment in the same container.

Goal: Practice replace() for swapping Fragments at runtime, not just loading one initially.

โ†’ Solution

Challenge 3: Fragment-to-Activity Communication

Add an interface OnDetailsClosedListener with a function onDetailsClosed() to DetailsFragment, implement it via onAttach with a safe cast, and add a "Close" button that calls it. Have MainActivity implement the interface and log a message when it's called.

Goal: Practice the full interface-callback communication pattern end to end.

โ†’ Solution

๐Ÿ’ก Fragments Feel Heavier at First โ€” That's Normal

Compared to an Activity, a Fragment involves more moving pieces (its own lifecycle, a hosting container, a manager to add/remove it) for what's conceptually "just a reusable piece of UI." That overhead pays for itself once an app has several screens sharing common pieces, or needs different screen arrangements on different device sizes โ€” exactly the kind of reuse a single-Activity design can't offer on its own.

๐ŸŽฏ What's Next

Next chapter: Navigation Component โ€” the nav graph, SafeArgs, deep links, and back stack management, building directly on this chapter's Fragments.