Layouts and Jetpack Compose
The XML view system and the Compose alternative: how a screen is described, measured and redrawn in each model.
The XML view hierarchy
In the classic toolkit a screen is a tree of View objects, usually declared in XML and inflated at runtime. Layout is a two-pass measure and layout walk down that tree.
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/title"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<Button
android:id="@+id/action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/press"
app:layout_constraintTop_toBottomOf="@id/title" />
</androidx.constraintlayout.widget.ConstraintLayout>0dpwith two opposing constraints means match the available space — the idiomatic way to stretch in ConstraintLayout.@string/and@color/references keep text and colours in resources so they can be translated and themed.- Deeply nested layouts cost real time on every frame.
ConstraintLayoutexists to flatten what would otherwise be nestedLinearLayouts.
Compose: UI as functions
Jetpack Compose replaces the view tree with composable functions. You call functions that describe the UI for the current state; when state changes, Compose re-runs only the functions that read it.
@Composable
fun Greeting(names: List<String>, modifier: Modifier = Modifier) {
Column(modifier = modifier.padding(16.dp)) {
names.forEach { name ->
Text(text = name, style = MaterialTheme.typography.bodyLarge)
}
}
}
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Tapped " + count + " times")
Button(onClick = { count++ }) { Text("Tap me") }
}
}Three ideas do most of the work. Composable functions are ordinary functions annotated and called only from other composables. State held with remember plus mutableStateOf makes a read trigger recomposition. Modifiers chain layout and appearance without wrapper views.
onValueChange callback instead of mutating inside the child — keeps components previewable and testable.Which one to use
| Aspect | XML views | Jetpack Compose |
|---|---|---|
| Unit of UI | View instance in a tree | Composable function call |
| Updating the UI | Find view, then mutate it | Change state, re-run the function |
| Layout model | Single measure, then layout | Multi-pass, no wrapper views needed |
| Reuse | Custom view, XML include | Extract a composable or use a slot parameter |
| Tooling | Layout editor, previews | Previews with @Preview and live edit |
- New screens in a modern app are usually Compose. Existing XML screens keep working and can host composables through
ComposeView. - Both models coexist in one app, which is why so many production codebases contain both.
- Prefer one mental model per screen. Mixing them inside a single screen is where most confusion starts.
FAQ
Is XML layout dead?
Why does my Compose screen recompose too often?
Related
Android projects and activities Permissions, storage and publishing
Last refreshed 2026-09-18.