Topic 10: Advanced UI Concepts (Themes/Styles/Localization)
📖 7 min read · 🎯 intermediate · 🧭 Prerequisites: fragments-intents-intent-filters, strings-in-java
Why this matters
Up until now, you've probably been setting colors, font sizes, and text styles directly on each view — one button here, one heading there. It works, but the moment your app has ten screens, changing a single brand color means hunting through dozens of files. And if someone asks "can we support Hindi?" you realize you've hardcoded every string. Themes, styles, and localization are how real Android apps stay consistent without that chaos — one change in one place, and everything updates. That's what we're building toward today.
What You'll Learn
- Define and apply application-wide and activity-specific themes in
res/values/styles.xmlandAndroidManifest.xml - Create reusable styles for individual UI elements and attach them in XML layouts
- Structure string resources for multiple locales and provide translated variants under
res/values-<locale>/ - Access localized strings from Java code using
getString() - Create locale-specific layout files when translated text requires different visual arrangements
The Analogy
Think of an Android theme as a city-wide zoning ordinance: it sets the rules for every building (activity and view) at once — primary color, action bar appearance, accent — so no individual contractor has to guess. Styles are like a building's interior design spec sheet: they describe the look of one component type (say, every TextView labeled "heading") in reusable, shareable detail. Localization is the city's translation bureau: the same street sign content exists in English on res/values/strings.xml and in Spanish on res/values-es/strings.xml, and Android's resource system automatically hands each resident the version written in their language.
Chapter 1: Themes in Android
A theme is a named collection of attributes that controls the visual identity of an entire app or a single activity. Rather than repeating colorPrimary on every view, you set it once in the theme and every widget that references it stays in sync automatically.
Defining a Theme
Themes live in res/values/styles.xml. They inherit from a parent theme and override only what differs:
<!-- res/values/styles.xml -->
<resources>
<!-- Base application theme -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<!-- Custom theme for a specific activity -->
<style name="CustomActivityTheme" parent="AppTheme">
<item name="android:windowBackground">@color/windowBackground</item>
</style>
</resources>
CustomActivityTheme inherits everything from AppTheme and only overrides windowBackground. This parent–child chain keeps overrides minimal and readable.
Applying a Theme
Assign themes in AndroidManifest.xml. The android:theme attribute on <application> sets the default for every activity; the same attribute on an individual <activity> overrides just that screen:
<!-- AndroidManifest.xml -->
<application
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:theme="@style/CustomActivityTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
flowchart TD
A["AppTheme\n(application-level)"] -->|"inherited by"| B["CustomActivityTheme\n(activity-level)"]
B -->|"overrides windowBackground"| C["MainActivity"]
A -->|"default for all other"| D["Other Activities"]
Chapter 2: Styles in Android
Where a theme governs the whole app, a style targets individual widget types. Think of styles as the Android equivalent of a CSS class: define attributes once, reference everywhere.
Defining a Style
Styles are also declared in res/values/styles.xml. They do not require a parent, though they can inherit one:
<!-- res/values/styles.xml -->
<resources>
<!-- Define a text style -->
<style name="CustomTextStyle">
<item name="android:textColor">@color/colorPrimary</item>
<item name="android:textSize">18sp</item>
<item name="android:fontFamily">sans-serif</item>
</style>
</resources>
The three attributes here — textColor, textSize, and fontFamily — will be applied uniformly to every view that references this style.
Applying a Style
Reference the style with android:style directly on any view in a layout file. Both TextView and Button below share identical typography through a single reference:
<!-- res/layout/activity_main.xml -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, World!"
android:style="@style/CustomTextStyle" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click Me"
android:style="@style/CustomTextStyle" />
</LinearLayout>
Changing CustomTextStyle in styles.xml updates both views instantly — no hunting through layouts required.
Chapter 3: Localization in Android
Localization (commonly abbreviated l10n) is the practice of making your app speak the user's language — and not just translating words, but also adapting layouts for text that expands or contracts across languages.
String Resources
All user-visible text belongs in XML resource files under res/values/, never hard-coded in Java or Kotlin. The default (English) file is at res/values/strings.xml. For each additional language you create a parallel directory named res/values-<BCP-47 language tag>/.
Default strings (English):
<!-- res/values/strings.xml -->
<resources>
<string name="app_name">MyApp</string>
<string name="welcome_message">Welcome to MyApp!</string>
<string name="button_text">Click Me</string>
</resources>
Localized strings (Spanish):
<!-- res/values-es/strings.xml -->
<resources>
<string name="app_name">MiAplicación</string>
<string name="welcome_message">¡Bienvenido a MiAplicación!</string>
<string name="button_text">Haz Clic</string>
</resources>
Android selects the correct file at runtime based on the device's locale setting. If no match is found, it falls back to the default res/values/strings.xml.
Accessing String Resources from Java
Use getString(R.string.<name>) to retrieve the locale-appropriate string in code:
// MainActivity.java
package com.example.myapp;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView welcomeTextView = findViewById(R.id.welcome_text);
welcomeTextView.setText(getString(R.string.welcome_message));
}
}
Android resolves R.string.welcome_message to the Spanish variant automatically when the device locale is es. Your Java code stays locale-agnostic.
Supporting Different Layouts
Some languages produce significantly longer or shorter text than English. When string expansion breaks a layout, create a locale-specific layout file under res/layout-<locale>/. Android picks the matching directory at runtime, exactly as it does for string resources.
Default layout (res/layout/activity_main.xml):
<!-- res/layout/activity_main.xml -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/welcome_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/welcome_message" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_text" />
</LinearLayout>
Spanish layout (res/layout-es/activity_main.xml):
<!-- res/layout-es/activity_main.xml -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/welcome_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/welcome_message" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_text" />
</LinearLayout>
Both layouts reference @string/welcome_message — the string resource system handles translation. The layout file exists separately only when the visual structure itself needs to differ (wider buttons, different padding, stacked vs. side-by-side arrangement).
flowchart LR
D["Device locale: es"] --> R["Android Resource System"]
R -->|"found res/values-es/"| S["Spanish strings.xml"]
R -->|"found res/layout-es/"| L["Spanish layout XML"]
R -->|"fallback"| E["res/values/strings.xml\n(English default)"]
🧪 Try It Yourself
Task: Add a French localization to an existing Android project.
- Create the directory
res/values-fr/and add astrings.xmlfile that translates at minimumapp_name,welcome_message, andbutton_textto French. - Change your device or emulator locale to Français (France) via Settings → General Management → Language.
- Launch the app.
Success criterion: The welcome TextView and button label render in French without any code change in MainActivity.java.
Starter file to copy:
<!-- res/values-fr/strings.xml -->
<resources>
<string name="app_name">MonApp</string>
<string name="welcome_message">Bienvenue sur MonApp !</string>
<string name="button_text">Cliquez ici</string>
</resources>
🔍 Checkpoint Quiz
Q1. What is the difference between a theme and a style in Android?
A) A theme is applied to the entire app or activity; a style is applied to individual views
B) A theme is defined in Java; a style is defined in XML
C) Themes are only for colors; styles handle layout dimensions
D) There is no difference — the terms are interchangeable
Q2. Given this snippet in AndroidManifest.xml, which theme does MainActivity use?
<application android:theme="@style/AppTheme">
<activity android:name=".MainActivity"
android:theme="@style/CustomActivityTheme" />
</application>
A) AppTheme, because the application-level attribute always wins
B) CustomActivityTheme, because the activity-level attribute overrides the application-level one
C) Both themes are merged together
D) Neither; android:theme is not a valid attribute on <activity>
Q3. A developer hard-codes android:text="Hello" directly on a TextView in the layout file instead of using @string/hello. What problem does this cause?
A) The app will crash at runtime
B) The text cannot be localized — it will always show "Hello" regardless of device language
C) The app will fail to compile
D) The text will be invisible
Q4. You have strings that are 40% longer in German than in English, causing buttons to clip. How do you fix this while keeping a single Java source file?
A) Create res/layout-de/activity_main.xml with wider button dimensions and reference the same @string/ resources
B) Create a separate MainActivity subclass for German
C) Increase the font size in res/values-de/styles.xml
D) Android automatically resizes buttons for all locales — no action needed
A1. A — Themes apply a consistent look to an entire app or activity; styles define the appearance of individual view instances.
A2. B — An android:theme on a <activity> tag overrides the one on <application> for that specific screen.
A3. B — Hard-coded strings bypass the resource system entirely, so getString() and locale-specific strings.xml files have no effect on that text.
A4. A — Creating res/layout-de/activity_main.xml lets Android inflate the wider layout on German devices while the same R.string.* references resolve to German text automatically.
🪞 Recap
- Themes are defined in
res/values/styles.xml, assigned inAndroidManifest.xml, and apply visual defaults to an entire app or a single activity. - Styles target individual views and work like CSS classes — define once in
styles.xml, reference withandroid:stylein any layout. - Localized string resources live in
res/values-<locale>/strings.xml; Android selects the correct file based on the device locale, falling back tores/values/strings.xml. getString(R.string.<name>)in Java always returns the locale-appropriate string without any conditional logic in code.- When translated text requires a different visual structure, create a matching layout under
res/layout-<locale>/— the string references inside still resolve via the resource system.
📚 Further Reading
- Android Developers — Styles and Themes — the official deep-dive on inheritance, overlays, and Material theming
- Android Developers — Localize your app — covers locale qualifiers, plurals, RTL support, and testing strategies
- Material Design — The color system — explains
colorPrimary,colorSecondary, and the full palette model used by modern Android themes - ⬅️ Previous: Strings in Java
- ➡️ Next: Collections in Java — List