The old way vs the Compose way
Android has had two completely different ways of putting things on a screen. Seeing what the old one felt like is the fastest way to understand why the new one is shaped the way it is.
The job every app has to do
You have an . Android has given your app a rectangle of screen. Now something has to appear in it.
For the first thirteen years of Android there was one way to do that. Since 2021 there is a second way, and it is not an improvement on the first one — it is the opposite approach. Google now recommends the new one for every new app, and it is what this whole course uses.
Both ways can build exactly the same screen. Here is the screen we are going to build twice: a number and a button that makes the number go up.
The same screen, whichever way you build it.
Imagine a scoreboard at a school sports day.
Way one. A person stands at the board with chalk. When a team scores, someone shouts the new number and the chalk-holder rubs out the old one and writes the new one. It works — as long as they hear every shout, rub out every old number, and never get distracted. Miss one and the board is now telling a lie, and nobody can tell just by looking whether it is right.
Way two. There is no chalk. You write the scores on a slip of paper and push it through a slot. A machine inside reprints the entire board from that slip, instantly, every time the slip changes. There is nothing to forget, because there is no "update the board" step at all. The board is simply a picture of the slip.
Way one is how Android used to work. Way two is .
Way one: describe the screen once, then edit it forever
The old approach is called the View system. You describe the screen in an XML file — a that lives in res/layout/:
1<LinearLayout
2 android:orientation="vertical"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent">
5
6 <TextView
7 android:id="@+id/scoreText"
8 android:layout_width="wrap_content"
9 android:layout_height="wrap_content"
10 android:text="Score: 0" />
11
12 <Button
13 android:id="@+id/addButton"
14 android:layout_width="wrap_content"
15 android:layout_height="wrap_content"
16 android:text="+1" />
17
18</LinearLayout>That file is a starting position. It says what the screen looks like at the moment it is created and nothing more. To make anything ever change, you write that reaches into the screen and edits it:
1var score = 0
2
3override fun onCreate(state: Bundle?) {
4 super.onCreate(state)
5 setContentView(R.layout.activity_main)
6
7 val scoreText =
8 findViewById<TextView>(R.id.scoreText)
9 val addButton =
10 findViewById<Button>(R.id.addButton)
11
12 addButton.setOnClickListener {
13 score = score + 1
14 scoreText.text = "Score: $score"
15 }
16}Read that last block carefully, because the problem is hiding in plain sight.
There are now two copies of the score. One is the score variable in your code. The other is the text sitting inside scoreText on the screen. They are separate things, and the only reason they ever agree is that line scoreText.text = "Score: $score", which you had to remember to write.
Forget it in one place out of twenty and you get the worst kind of bug: the app is right, the screen is wrong, and nothing crashes. This style is called imperative UI — you issue orders, one at a time, and the screen obeys each one.
Way two: describe the screen as a function of the data
Compose deletes the XML file and deletes findViewById. Instead you write a function that describes what should be on screen for the data you have right now:
1@Composable
2fun ScoreCounter() {
3 var score by remember { mutableStateOf(0) }
4
5 Column(
6 modifier = Modifier.padding(24.dp),
7 horizontalAlignment =
8 Alignment.CenterHorizontally
9 ) {
10 Text(text = "Score: $score")
11 Button(onClick = { score++ }) {
12 Text("+1")
13 }
14 }
15}Nine lines, one file, one copy of the score.
That is the whole idea, and it is worth saying in one sentence:
You never update the screen. You change the data, and the screen is a description of the data.
This style is called declarative. You declare what should be true; the system works out what to change on the glass. It is the same trick a spreadsheet plays. You do not tell cell C1 to redraw when A1 changes — you write =A1*2 once, and it just keeps being correct.
What you actually gain
| The old way | Compose |
|---|---|
| XML file and Kotlin file | One Kotlin file |
| Two copies of every changing value | One copy |
| You remember to update the screen | Compose updates it |
| Reuse means a custom View class | Reuse means calling a function |
| A bug means "the display is out of date" | That bug category does not exist |
The reuse row matters more than it looks. In the old system, making a reusable piece of screen — say a player's score panel — meant writing a whole class, and most people did not bother, so layouts got copy-pasted. In Compose a reusable piece of screen is a function, and you already know how to write functions. You will make dozens.
The View system is not dead. Millions of apps still use it, and Android itself is still built on it underneath — Compose ultimately draws into a single old-style View. But nothing in this course uses XML , and if you start a new app in 2026, neither should you.
Where Compose plugs into your app
You already met MainActivity in Part 2. Here it is in a Compose app, complete:
1class MainActivity : ComponentActivity() {
2 override fun onCreate(state: Bundle?) {
3 super.onCreate(state)
4 setContent {
5 ScoreCounter()
6 }
7 }
8}setContent { } is the doorway. Everything inside those braces is Compose; everything outside is ordinary Android. One , one setContent, and from there down it is all just functions calling functions.
Notice what is missing: no setContentView, no R.layout anything, no to wire views to variables. That is roughly two hundred lines you will never write.
No typing this time — this is a two-minute look at real code.
- Open Pocket Studio and tap Projects.
- Open the project you built in Part 0.
- In the file tree, open
app/src/main/java/…/MainActivity.kt. - Find the line that says
setContent {. Everything indented under it is Compose. - Now look at the file tree again and open
app/src/main/res/. Look for a folder calledlayout. There isn't one. That is the clearest single sign that this is a Compose app. - Tap the Run button (the triangle) once, just to confirm the project still builds. Then come back — Lesson 3.2 starts here.
setContent. It is not built into ComponentActivity — it arrives with the activity-compose library and needs its own import.import androidx.activity.compose.setContent at the top of the file. If it still fails, check that app/build.gradle.kts contains implementation(libs.androidx.activity.compose).setContentView(R.layout.activity_main). Your project has no res/layout folder, so there is no generated R.layout entry to point at.setContentView(...) line and use setContent { ... } instead. A Compose project does not have layout XML at all.R.layout.*. In Compose the screen is described by a Kotlin function, not by a resource file.Text is a composable supplied by Material 3, and Pocket Studio has not imported it for you yet.import androidx.compose.material3.Text at the top of the file. Almost every Compose error in your first week is a missing import — get comfortable with them now.- The old View system is imperative: an XML plus Kotlin that reaches in and edits the screen with
findViewById. Every changing value exists twice. - is declarative: a Kotlin function describes the screen for the data you have, and Compose keeps the glass matching the description.
- You never write "now update the screen". You change the data.
- A reusable piece of screen is just a function, so reuse costs almost nothing.
setContent { }inside your is where Compose begins.- Next: writing your own composable function from scratch, and seeing it appear on your phone.