Module 7 of 20

UI Development (XML & Jetpack Compose)

Compare XML layouts and modern declarative Jetpack Compose UI frameworks, layout design, and component rendering.

Module 7: UI Development (XML & Jetpack Compose)

Learning Objectives

By the end of this module, you’ll understand:

  • How Android renders a UI
  • The View hierarchy
  • The rendering pipeline (Measure → Layout → Draw)
  • XML layouts and their role
  • LayoutManagers (LinearLayout, FrameLayout, ConstraintLayout)
  • View Binding vs Data Binding
  • Event handling
  • Jetpack Compose architecture
  • Declarative vs Imperative UI
  • State and recomposition
  • Performance considerations
  • Best practices

Part 1 – The Android View System


1. What Is a View?

Everything you see on an Android screen is a View.

Examples:

Button

TextView

ImageView

EditText

RecyclerView

Switch

ProgressBar

All of them inherit from the same base class:

View

Think of View as the fundamental building block of the Android UI toolkit.


The View Hierarchy

Imagine this screen:

+-------------------------+
| Welcome                 |
|                         |
| Username [________]     |
| Password [________]     |
|                         |
|   [ Login ]             |
+-------------------------+

Internally, Android represents it as a tree.

ConstraintLayout

├── TextView
├── EditText
├── EditText
└── Button

Every Android screen is a tree.

Not a list.

Not a graph.

A tree.


2. Why a Tree?

Suppose you have:

ConstraintLayout



LinearLayout



Button

If the parent moves:

The child moves.

If the parent becomes invisible:

The child disappears.

If the parent rotates:

The child rotates.

This parent-child relationship makes UI management predictable.


3. ViewGroups

Some Views contain other Views.

These are called ViewGroups.

View



ViewGroup



ConstraintLayout



Children

Examples:

  • ConstraintLayout
  • LinearLayout
  • FrameLayout
  • RelativeLayout (legacy)

A Button cannot contain another View.

A ConstraintLayout can.


4. XML Layouts

Instead of creating every View in Kotlin:

val button = Button(this)
button.text = "Login"

Android lets us describe UI declaratively in XML.

<Button
    android:text="Login"/>

The XML file is not the UI.

It is a blueprint.

During runtime:

activity_main.xml



LayoutInflater



Real Views



Displayed

5. LayoutInflater

One of Android’s most important classes.

When Activity executes:

setContentView(R.layout.activity_main)

Internally:

XML



LayoutInflater



Creates View Objects



Returns Root View

The XML is parsed.

Real Kotlin/Java objects are created.


6. The Three Rendering Phases

This is one of the most important concepts in Android UI.

Every View goes through three phases.

Measure



Layout



Draw

Every frame.

Every screen.

Every View.


Phase 1 — Measure

Question:

“How big do you want to be?”

The parent asks every child.

Example:

Button



Width?



Height?

The Button answers.

Maybe:

120dp

48dp

Every View measures itself.


Phase 2 — Layout

Now the parent asks:

“Where should you be positioned?”

Example:

Button



x = 30

y = 200

The View now knows its location.


Phase 3 — Draw

Finally:

Android renders pixels.

Canvas



Button



Text



Background



Border

Only now does the user actually see something.


7. The Complete Rendering Pipeline

XML



Inflate Views



Measure



Layout



Draw



Display

This happens far more often than many developers realize.

Changing a View’s size triggers another measure/layout pass.

Changing only its text color usually triggers just a redraw.

Understanding this helps you write efficient UI code.


8. Common Layouts


LinearLayout

Children arranged in one direction.

Vertical:

Title



Username



Password



Login

Horizontal:

Icon  Text  Arrow

Simple.

Easy.

Limited.


FrameLayout

Think of stacked paper.

Image



Progress Bar



Text

Children overlap.

Great for:

  • Fragment containers
  • Image overlays
  • Loading indicators

ConstraintLayout

The most powerful XML layout.

Instead of nesting:

Linear



Linear



Linear



Linear

Everything becomes constrained.

Button



Constrained



Parent Bottom

Advantages:

  • Flat hierarchy
  • Better performance
  • Flexible positioning

Most XML-based production apps use ConstraintLayout as the root layout.


9. Why Deep View Hierarchies Hurt Performance

Imagine:

LinearLayout



LinearLayout



LinearLayout



LinearLayout



Button

Each level:

  • Measures
  • Lays out
  • Draws

Deep trees increase work.

Flatter hierarchies are generally faster.

This is one reason ConstraintLayout became popular.


10. Finding Views

Old approach:

findViewById(R.id.button)

Problems:

  • Verbose
  • Unsafe casts (before generics improvements)
  • Boilerplate

Modern approach:

View Binding.

binding.loginButton.text = "Login"

Compile-time safety.

No manual lookup.


11. View Binding

Generated automatically.

Suppose:

activity_main.xml

Android generates:

ActivityMainBinding

Instead of:

findViewById(...)

You write:

binding.loginButton

Benefits:

  • Type safety
  • No null checks for existing views
  • Less boilerplate

12. Event Handling

A View receives user input.

Example:

button.setOnClickListener {

}

Flow:

User Touch



Android



Button



Click Listener



Your Code

Notice again:

Android delivers the event.

You react.


Part 2 – Jetpack Compose

Now we shift to modern Android.


13. Why Compose Exists

Imagine updating one TextView.

Old View system:

Find View



Modify View



Invalidate



Redraw

As UIs became more dynamic, manually keeping Views in sync with data became increasingly complex.

Google introduced Compose.


14. Imperative vs Declarative UI

XML / View System

Imperative.

You tell Android:

“Do this.”

Example:

textView.text = "Hello"
button.visibility = View.GONE

You manually mutate existing UI objects.


Compose

Declarative.

You describe:

“Given this state, the UI should look like this.”

Example:

@Composable
fun Greeting(name: String) {
    Text("Hello $name")
}

You describe the desired UI, not the steps to update it.


15. Composable Functions

Every UI element is a function.

@Composable
fun LoginScreen() {

}

Unlike Activities or Fragments:

Composable functions don’t own windows.

They emit UI into a composition managed by Compose.


16. Compose UI Tree

Just like XML has a View tree,

Compose builds a composition tree.

Column



Text



Button



Image

Conceptually similar.

Implementation completely different.


17. State

State is the heart of Compose.

Imagine:

var count = 0

Changing it won’t update the UI.

Compose needs observable state.

var count by remember {
    mutableStateOf(0)
}

Now:

State Changes



Compose Notices



Recomposition



UI Updates

18. Recomposition

This is Compose’s superpower.

Example:

Text("$count")

When:

count++



State Changed

Compose recomposes only the parts of the UI that depend on count.

Not the entire screen.


19. Remember

Without:

val name = ""

Every recomposition recreates the variable.

With:

remember {
    mutableStateOf("")
}

The value survives recompositions (but not Activity recreation).

Think of remember as “retain this value while this composable remains in the composition.”


20. Modifier

Instead of LayoutParams:

Compose uses Modifiers.

Modifier
    .padding(16.dp)
    .fillMaxWidth()

Modifiers are immutable objects that decorate how a composable is laid out, drawn, or interacts with input.


21. Compose Layouts

Instead of XML:

<LinearLayout>

Compose:

Column {

}

Horizontal:

Row {

}

Stack:

Box {

}

Constraint-based layouts are also available in Compose when needed, but Column, Row, and Box cover many common layouts.


22. LazyColumn

Equivalent of RecyclerView.

Old:

RecyclerView



Adapter



ViewHolder

Compose:

LazyColumn {

}

Compose creates and disposes visible items lazily, similar to how RecyclerView recycles views, though the internal implementation differs.


23. XML vs Compose

XMLCompose
Imperative updatesDeclarative UI
XML + KotlinKotlin only
View hierarchyComposition tree
findViewById / View BindingDirect function calls
Manual updatesState-driven updates
RecyclerViewLazyColumn
LayoutInflaterComposition engine

24. Common Compose Mistakes

Mistake 1

Keeping UI state in a normal variable.

Use observable state (mutableStateOf, StateFlow, etc.).


Mistake 2

Doing expensive work inside a composable.

Composable functions may execute many times due to recomposition.

Move long-running work to a ViewModel or side-effect APIs like LaunchedEffect.


Mistake 3

Thinking recomposition recreates everything.

Compose skips unchanged parts whenever possible.

Recomposition is generally much more granular than redrawing an entire screen.


25. Modern Architecture

A typical Compose app:

Activity



NavHost



Composable



ViewModel



Repository



Network

Notice how the Activity becomes much thinner.

Its main responsibility is hosting the Compose UI.


Best Practices

  • Keep your View hierarchy shallow.
  • Prefer ConstraintLayout over deeply nested XML layouts when using the View system.
  • Use View Binding instead of findViewById().
  • Keep business logic out of Activities, Fragments, and composables.
  • Make UI a function of state.
  • Hoist state to the appropriate owner (often a ViewModel).
  • Treat composables as lightweight, side-effect-free descriptions of UI whenever possible.

Mental Model

The biggest conceptual shift in this module is this:

View System

Data Changes



Developer Updates UI



UI Changes

You tell Android how to update the interface.


Compose

Data Changes



State Changes



Compose Recomposition



UI Automatically Matches State

You describe what the UI should look like for a given state.

This single shift—from imperative UI manipulation to declarative state-driven UI—is why Compose feels so different and why many developers find it more productive once they embrace its model.


Interview Questions

  1. What is the difference between a View and a ViewGroup?
  2. Explain the Measure → Layout → Draw pipeline.
  3. Why are deep View hierarchies bad for performance?
  4. What does LayoutInflater do?
  5. Compare LinearLayout, FrameLayout, and ConstraintLayout.
  6. Why is View Binding preferred over findViewById()?
  7. Explain the difference between imperative and declarative UI.
  8. What is recomposition in Jetpack Compose?
  9. What is the purpose of remember?
  10. How does LazyColumn differ conceptually from RecyclerView?

Next Module: Navigation & Intents (One of Android’s Core Concepts)

We’ll answer questions like:

  • What exactly is an Intent?
  • How does Android know which Activity to launch?
  • What’s the difference between explicit and implicit intents?
  • How do apps communicate with each other?
  • What are tasks, back stacks, and launch modes?
  • How does the Navigation Component work?
  • How does navigation differ between XML-based apps and Compose apps?

This module ties together Activities, Fragments, and the Android system itself, and it’s essential for understanding how Android applications move users through different parts of an app.