Pocket Studio Academy
HomePart 22.4

Resources: strings, colours, drawables

Full course10 min read·4 questions

Everything in your app that is not code lives in the res folder. Learn why text belongs there rather than in your Kotlin, how folder name suffixes let Android swap resources for dark mode or another language, and what the R class actually is.

The folder that makes translation possible

Here are two ways to put a title on a screen.

two ways to write a titlekotlin
1// The obvious way.
2Text("Dice Duel")
3
4// The Android way.
5Text(stringResource(R.string.app_name))

The first is shorter and reads better. The second is what every real app does, and it is worth understanding why before you decide the first one is fine.

The moment your app has a second language, a dark mode, or a tablet layout, the first version has to be found and changed by hand in every file it appears in. The second version does not change at all — you add one file, and Android picks it.

Think of it like this

Think about a theatre that runs the same play in two versions.

The script says "the drawing room". It does not say "the drawing room with the blue wallpaper". Backstage there are two complete sets built for that scene: a bright daytime one and a gloomy evening one. When the curtain goes up, the stage manager wheels out whichever the performance calls for.

The script never changes. Only the set does.

work exactly like that. Your code names the thing it wants. Android looks at the phone right now — dark mode? French? big screen? — and wheels out the matching version.

What lives in res

Here is the complete res folder from Dice Duel. It is small on purpose.

text
1app/src/main/res/
2  drawable/ic_launcher_foreground.xml
3  mipmap-anydpi-v26/ic_launcher.xml
4  mipmap-anydpi-v26/ic_launcher_round.xml
5  values/ic_launcher_background.xml
6  values/strings.xml
7  values/themes.xml
8  values-night/themes.xml

Three kinds of thing are in there.

  • values/ — anything that is really just a name and a value: strings, colours, dimensions, styles. Several per file.
  • drawable/ — pictures. One per file.
  • mipmap/ — launcher icons only. It is a separate folder purely so that the icon survives when unused screen densities are stripped out of a shipped app.

Strings

The simplest resource file in Android:

res/values/strings.xmlxml
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
3  <string name="app_name">Dice Duel</string>
4  <string name="roll">Roll</string>
5  <string name="player_one">Player 1</string>
6</resources>

Two ways to reach those, depending on where you are:

FromWritten as
XML (manifest, themes)@string/app_name
ComposestringResource(R.string.app_name)
Plain Kotlin in an ActivitygetString(R.string.app_name)

The @ in XML means "look this up". The R. in Kotlin means the same thing.

What R actually is

R is not something Google wrote. The build generates it, fresh, from your own files, every time you build.

reads every file under res, gives each resource a number, and writes a Kotlin- and Java-visible class listing them:

generated — never edit thiskotlin
1// Roughly what the build writes for you.
2object R {
3  object string {
4    const val app_name = 0x7f0e001c
5    const val roll = 0x7f0e0021
6  }
7}

Two useful consequences of knowing that.

First, R.string.app_name is checked by the . Misspell it and the build fails immediately, instead of the app crashing in front of a user. A raw "Dice Duel" string has no such protection.

Second, when you see Unresolved reference: R in a file that was fine yesterday, it almost always means the could not be generated — because some other resource file has an error in it. The real problem is somewhere in res, not in the file the editor is shouting about.

Qualifiers — the actual magic

This is the part that pays for all the indirection.

Add a suffix to a folder name and Android will use that folder only in the matching situation. Same resource names inside; different values.

FolderUsed when
values/Always, unless something better matches
values-night/The phone is in dark mode
values-fr/The phone's language is French
values-sw600dp/The screen is at least 600dp wide — a tablet
drawable-hdpi/The screen is roughly 1.5× density
layout-land/The phone is in landscape

Dice Duel uses exactly one -night:

res/values-night/themes.xmlxml
1<style
2  name="Theme.DiceDuel"
3  parent="android:Theme.Material.NoActionBar">
4  <item name="android:windowBackground">#FF121022</item>
5</style>

Same style name, darker colour, different parent. Your code never mentions night mode anywhere. Android picks the file.

And now Lesson 2.2 clicks into place: this is why rotation destroys your Activity. Changing the phone's language, orientation or theme changes which folder wins. Rebuilding the screen is how Android re-runs the picking.

A theme, line by line

themes.xml is where a lot of beginners get lost, because in a Compose app it does much less than it looks like it should.

Note

Honest warning about a genuine confusion. In a Compose app your app colours, fonts and shapes live in Kotlin, in ui/theme/Theme.kt. This XML theme only styles the window around Compose. Two files with "theme" in the name, doing different jobs. Lesson 3.11 builds the Kotlin one.

Drawables

A is anything Android can draw. It can be a PNG, but on Android it is far more often XML describing shapes:

res/drawable/ic_triangle.xmlxml
1<vector
2  xmlns:android="http://schemas.android.com/apk/res/android"
3  android:width="24dp"
4  android:height="24dp"
5  android:viewportWidth="24"
6  android:viewportHeight="24">
7  <path
8    android:fillColor="#FFFFFF"
9    android:pathData="M12 2 L22 22 L2 22 Z" />
10</vector>

That is a : a triangle described as instructions rather than pixels. It is about 250 bytes, it is razor sharp at any size, and it needs no separate copy per screen density. For icons, always prefer this to a PNG. Lesson 6.1 draws a real app icon this way.

The naming rules

Resource file names are strict, and the error message when you break the rule is not friendly.

  • Lowercase letters, digits and underscores only.
  • Must start with a letter.
  • No spaces, no dashes, no capitals.

dice_face_six.xml is fine. Dice Face 6.xml gives you three separate errors at once.

Try it in Pocket Studio
  1. Open Pocket Studio, tap Projects, open your Part 0 app.
  2. Tap Editor, open the file tree, and open app/src/main/res/values/strings.xml.
  3. Add a second string on its own line inside <resources>: <string name="tagline">Built on a phone</string>
  4. Open your main Compose file and show it. Add Text(stringResource(R.string.tagline)) next to your existing text, and add the import androidx.compose.ui.res.stringResource if the editor asks.
  5. Tap Build, press Run. The new text appears.
  6. Now make it lie about its own name. Change the reference to R.string.taglin and press Run again. Read the error — the build stops before the app is ever built.
  7. Put it back, then create the folder app/src/main/res/values-night/ and inside it a file strings.xml containing the same <resources> wrapper and <string name="tagline">Built on a phone, at night</string>.
  8. Press Run, then switch your phone to dark mode from the quick settings. The text changes. You did not write a single if.
Error Doctor5 common errors
AAPT: error: resource string/taglin (aka com.nativeworks.diceduel:string/taglin) not found.
MeansSomething asked for a resource name that does not exist anywhere under res. Usually a typo, sometimes a file you renamed or deleted.
FixCheck the spelling against res/values/strings.xml. Names are case-sensitive, and the error text shows exactly what was asked for.
Execution failed for task ':app:processDebugResources'. > A failure occurred while executing com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask$TaskAction
MeansThe resource compiler failed. This line is only the wrapper — it is the build saying "the resource step went wrong", not what went wrong.
FixScroll up in the build output. The real cause is an AAPT: error: line a few lines above, and it names the file and resource.
AAPT: error: invalid file name: must contain only lowercase letters, digits, '_', or '.'
MeansA file under res has a capital letter, a space or a dash in its name. Android's resource names become Kotlin identifiers, so they follow Kotlin's rules.
FixRename the file to lowercase with underscores — Dice Face.png becomes dice_face.png — then update anywhere that referred to the old name.
AAPT: error: duplicate value for resource 'string/app_name' with config ''. AAPT: error: resource previously defined here.
MeansThe same resource name is defined twice in the same configuration — usually because it was pasted into strings.xml twice, or exists in two different files under values/.
FixSearch your res/values folder for the name and delete one of the two. The second error line points at the first definition.
e: Unresolved reference: R
MeansThe was never generated, so nothing can refer to a resource. Almost always because a different resource file failed to compile, which stopped R being written at all.
FixIgnore this line and look for an AAPT: error: higher up the build output. Fix that, and R reappears. If there is genuinely no other error, check that the import at the top of your file matches your package name.
Recap
  • Everything that is not code lives in res: in values/, in drawable/, launcher icons in .
  • Refer to them with @string/name from XML and R.string.name from Kotlin. The is generated from your own files on every build.
  • like -night, -fr and -sw600dp let Android pick a different version with no if in your code — which is exactly why a rebuilds a screen.
  • beat PNGs for icons: tiny, sharp at every size, one file.
  • Resource names are lowercase, digits and underscores only.
  • Next: the free lesson everyone asks for — what Gradle actually is, in plain English.