Light/Dark Theme (Java)

Android has built-in support for day/night theming — an app can define one set of colors for light mode and a completely different set for dark mode, and the system automatically shows the right one based on the device's current setting. This is the same resource-qualifier idea introduced in Resources Overview, applied specifically to night mode.

1. Light and Dark Color Resources

The default res/values/colors.xml holds your light-mode colors, exactly as usual. A parallel folder, res/values-night/colors.xml, defines resources with the same names but dark-mode-appropriate values. Android picks whichever file matches the active system setting — no code required to switch between them.

<!-- res/values/colors.xml (light mode) -->
<resources>
    <color name="screen_background">#FFFFFF</color>
    <color name="primary_text">#1A1A1A</color>
    <color name="card_background">#F5F5F5</color>
</resources>
<!-- res/values-night/colors.xml (dark mode) -->
<resources>
    <color name="screen_background">#121212</color>
    <color name="primary_text">#EEEEEE</color>
    <color name="card_background">#1E1E1E</color>
</resources>
<!-- RecipeBox's recipe detail screen root layout -->
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/screen_background">

    <TextView
        android:id="@+id/tvRecipeTitle"
        android:textColor="@color/primary_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>
Explanation:
  • The layout XML never changes — it always references @color/screen_background and @color/primary_text.
  • Android resolves those names to the light or dark values depending on the active system theme, with zero Java code involved.
  • This is the exact same qualifier mechanism as values-night for colors that other qualifiers use for language or screen size — just applied to the night-mode condition instead.

2. Letting the User Override the System Setting

Some users want an app to always stay dark, regardless of their phone's overall setting; others want to follow the system automatically. AppCompatDelegate.setDefaultNightMode() lets your app apply that choice at runtime, overriding — or following — the device setting.

// SettingsActivity.java — a Light / Dark / System Default toggle
public class SettingsActivity extends AppCompatActivity {

    private RadioGroup themeGroup;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_settings);

        themeGroup = findViewById(R.id.rgTheme);

        themeGroup.setOnCheckedChangeListener((group, checkedId) -> {
            if (checkedId == R.id.rbLight) {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
            } else if (checkedId == R.id.rbDark) {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
            } else {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
            }
        });
    }
}
Explanation:
  • MODE_NIGHT_NO forces light mode; MODE_NIGHT_YES forces dark mode; MODE_NIGHT_FOLLOW_SYSTEM defers to the device setting.
  • Calling setDefaultNightMode() recreates all currently open Activities so the new theme applies immediately — the user sees the switch happen right away.
  • RecipeBox persists this choice with SharedPreferences (covered in the upcoming SharedPreferences lesson) so the preference survives an app restart.

3. Testing Dark Mode

You don't need to keep flipping your phone's system setting to check both themes. Android Studio's Layout Editor preview panel has a "Night" toggle that renders the same layout with the values-night resources applied, letting you compare light and dark side by side. The emulator also has a quick-settings toggle for dark theme, and on a physical device it's under system Display settings — useful for a final check, but the in-editor preview is faster during day-to-day layout work.

4. android:forceDarkAllowed — An Older Approach

Android briefly offered android:forceDarkAllowed="true" on the app theme, which asked the system to automatically darken an app's existing light-mode UI on the fly — no values-night resources needed. It sounds convenient, but the results are often imprecise: the system is guessing at which colors to invert or darken, and it can produce washed-out cards, oddly-inverted images, or poor contrast in places you never explicitly designed for dark mode.

Why explicit night resources win: defining your own values-night/colors.xml (as this lesson covers) gives you exact control over every color in dark mode — the RecipeBox background, card, and text colors above were each chosen deliberately for readability and contrast, not auto-generated by an algorithm. Force-dark is a reasonable emergency fallback for an app that has never been updated, but a deliberately designed dark theme always looks more polished.

Light vs. Dark Resource Resolution

System theme = Light res/values/colors.xml → used System theme = Dark res/values-night/colors.xml → used (falls back to values/ if a color is missing here) App override via AppCompatDelegate MODE_NIGHT_YES / MODE_NIGHT_NO → forces one or the other regardless of system setting MODE_NIGHT_FOLLOW_SYSTEM → back to following the device

Troubleshooting

Dark mode not applying? → Confirm your app theme extends a DayNight variant, e.g. Theme.MaterialComponents.DayNight, in your manifest/theme.

Some colors look wrong only in dark mode? → Check that every color name used in a layout has a matching entry in values-night/colors.xml; a missing one silently falls back to the light value.

Toggle doesn't change theme immediately? → setDefaultNightMode() only recreates Activities that are currently in the back stack; a freshly launched Activity will already show the new theme regardless.

With theming handled for both light and dark, the next lesson, Localization & RTL, covers adapting an app's text and layout direction for different languages.