Skip to content
beginner Phase 3 · UI with XML & Views

Themes & Styles

Customize app appearance with themes, styles, and dark mode support.

35m
0 problems
Topic Progress 0%

Themes vs Styles vs Attributes

The Three Levels of Customization

Android's theming system has three layers, each serving a distinct purpose:

  1. Theme — a collection of attributes applied to an entire Activity or Application. It defines the overall look: colors, typography defaults, component defaults.
  2. Style — a reusable set of attributes applied to individual views. It defines how a specific component looks.
  3. Attribute — a single customizable property (like colorPrimary or android:textSize).

How They Relate

A theme provides default values for attributes. A style overrides specific attributes for individual views. When you set a style on a view, the view first inherits from the theme, then applies the style on top:

<!-- Theme defines the default button text color -->
<style name="Theme.MyApp" parent="Theme.Material3.DayNight">
    <item name="colorPrimary">@color/blue_500</item>
</style>

<!-- Style overrides for a specific button variant -->
<style name="Widget.MyApp.Button.Danger">
    <item name="backgroundTint">@color/red_500</item>
    <item name="android:textColor">@color/white</item>
</style>

<!-- Button inherits from theme, then applies the style -->
<com.google.android.material.button.MaterialButton
    style="@style/Widget.MyApp.Button.Danger"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Delete" />

Inheritance in Themes and Styles

Both themes and styles support inheritance using dot notation:

<!-- Parent theme -->
<style name="Theme.MyApp" parent="Theme.Material3.DayNight">
    <item name="colorPrimary">@color/blue_500</item>
</style>

<!-- Child theme inherits everything, overrides one attribute -->
<style name="Theme.MyApp.Red" parent="Theme.MyApp">
    <item name="colorPrimary">@color/red_500</item>
</style>

Apply different themes to activities:

<activity android:name=".MainActivity"
    android:theme="@style/Theme.MyApp" />

<activity android:name=".DangerZoneActivity"
    android:theme="@style/Theme.MyApp.Red" />

Defining Custom Attributes

For complex components that need custom properties, define attributes in res/values/attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="RatingBar">
        <attr name="maxRating" format="integer" />
        <attr name="filledStar" format="reference" />
        <attr name="emptyStar" format="reference" />
    </declare-styleable>
</resources>

Reference them in a style:

<style name="Widget.MyApp.RatingBar">
    <item name="maxRating">5</item>
    <item name="filledStar">@drawable/star_filled</item>
    <item name="emptyStar">@drawable/star_empty</item>
</style>

Read them in a custom view constructor:

val attrs = context.obtainStyledAttributes(set, R.styleable.RatingBar)
val maxRating = attrs.getInt(R.styleable.RatingBar_maxRating, 5)
attrs.recycle()

Dark Mode with DayNight

How Dark Mode Works

Android 10+ supports system-wide dark mode. The DayNight theme reads the system setting and swaps resource qualifiers automatically:

  • res/values/colors.xml — light mode colors (default)
  • res/values-night/colors.xml — dark mode colors

Setting Up DayNight

<style name="Theme.MyApp" parent="Theme.Material3.DayNight">
    <!-- Colors used in both modes -->
    <item name="colorPrimary">@color/primary</item>
</style>

Define separate color files:

<!-- res/values/colors.xml -->
<resources>
    <color name="primary">#1976D2</color>
    <color name="background">#FFFFFF</color>
    <color name="onBackground">#1C1B1F</color>
</resources>

<!-- res/values-night/colors.xml -->
<resources>
    <color name="primary">#90CAF9</color>
    <color name="background">#1C1B1F</color>
    <color name="onBackground">#E6E1E5</color>
</resources>

Material Color Scheme Attributes

Material Design 3 defines a set of standard color attributes that map to specific roles:

Attribute Role
colorPrimary Brand color, used for key components
colorOnPrimary Text/icon color on top of colorPrimary
colorSecondary Accent color for less prominent components
colorSurface Background for cards, sheets, menus
colorOnSurface Text on surfaces
android:colorBackground App-level background

Reference these attributes in your views instead of hardcoded colors:

<!-- GOOD — adapts to theme and dark mode -->
<TextView
    android:textColor="?attr/colorOnBackground"
    android:background="?attr/colorSurface" />

<!-- BAD — hardcoded, breaks in dark mode -->
<TextView
    android:textColor="#000000"
    android:background="#FFFFFF" />

Enabling Force Dark for Legacy Apps

If you have an existing app without Night resources, enable Force Dark in the Activity:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_YES
}

This applies a dark overlay to your existing colors. It works but produces inconsistent results — define proper Night resources instead.

Testing Dark Mode

Use Android Studio's Theme Editor (Tools → Theme Editor) to preview both modes. On a real device, toggle dark mode in System Settings → Display. Test your app in both states to catch hardcoded colors that don't adapt.

Quiz

1. What is the difference between a theme and a style in Android?

Question 1 options

2. Where should you define colors for dark mode?

Question 2 options

3. Why should you use ?attr/colorOnBackground instead of #000000 for text color?

Question 3 options

4. How do you override a theme attribute for a single Activity?

Question 4 options

Flashcards

Question

What is the hierarchy: theme → style → attribute?

Answer

Theme defines defaults for an Activity. Style overrides attributes for individual views. Attribute is a single customizable property like colorPrimary.

Question

How does Android handle dark mode resources?

Answer

Use the -night resource qualifier. Define colors in res/values/colors.xml for light mode and res/values-night/colors.xml for dark mode. The system selects automatically.

Question

What is the difference between colorPrimary and colorOnPrimary?

Answer

colorPrimary is the brand color used for key components. colorOnPrimary is the text/icon color that appears on top of colorPrimary surfaces.

Question

What parent theme should you extend for Material Design 3?

Answer

Theme.Material3.DayNight — it supports both light and dark mode with Material Design 3 defaults.

Revision Notes

Key Takeaways

  • 1. Themes set defaults for an Activity; styles override for individual views — they compose, not replace
  • 2. Always reference theme attributes (?attr/colorOnBackground) instead of hardcoded colors
  • 3. Dark mode is automatic when using DayNight theme with -night resource qualifiers
  • 4. Material color attributes (colorPrimary, colorSurface) ensure consistent theming across components
  • 5. Define custom attributes in attrs.xml when building reusable custom components

Interview Tips

  • Explain the theme → style → attribute inheritance chain clearly
  • Be ready to describe how DayNight theme swaps resources automatically
  • Know the Material color attribute roles (Primary, Surface, OnSurface)
  • Discuss how custom attributes work with obtainStyledAttributes for custom views

Cheat Sheet

Themes & Styles Cheat Sheet

Hierarchy: Theme → Style → Attribute

  • Theme: applied to Activity/Application via manifest or setTheme()
  • Style: applied to individual views via style attribute
  • Attribute: single customizable property

Inheritance:

<style name="Theme.MyApp.Red" parent="Theme.MyApp">
    <item name="colorPrimary">@color/red</item>
</style>

Dark Mode:

  • Use Theme.Material3.DayNight as parent
  • Light: res/values/colors.xml
  • Dark: res/values-night/colors.xml
  • Reference ?attr/colorOnBackground, not hardcoded #000000

Key Material Color Attributes:

  • colorPrimary — brand color
  • colorOnPrimary — text on primary
  • colorSurface — card/sheet background
  • colorOnSurface — text on surfaces
  • android:colorBackground — app background

Per-Activity Theme Override:

  • Manifest: android:theme on
  • Code: setTheme() before setContentView()