Android10.0 Feature - Dark Mode, Dark Theme

Posted by kanikilu on Fri, 14 Jan 2022 23:05:16 +0100

Thematic styles about projects are a common feature, especially in app s of some music types, and when I watched Android 10 before, I said that the new feature included Dark Mode, which is just a chance to talk about it together (recently busy, messy and can only be charged in the middle of the night)

If there is a need for skin replacement in the future, wait for me to exchange the triple shelf for a flower tiger, sleep, sleep~

Simple analysis

If you were to implement a day mode, a night mode, how would you do that?

Hmm... I remember thinking the following many years ago

  • How many different themes, patterns, how many different layouts I write, in short how many patterns, how many layouts I write (OK, maybe android did this many years ago, but if it still does, it's too inefficient and degenerate - not recommended at all)
  • Listen for subject change events, wrap variations in the base class, and then set the subclasses individually (Hmm... a little better than what you thought at first, but a few interfaces work. If you have more project design interfaces and are complex to teach, you need to adapt for almost every Activity or View settings class, which also generates a lot of junk code - not recommended)
  • Dynamically adapts the display state of UI in different modes through Theme

So let's first analyze what is involved when switching modes?

  • The overall layout color must change, not to mention black, at least to a dark tone, so that means we have two sets of colors to prepare for switching between different scenes
  • Secondly, for some of our needs, we need to change the pictures in different modes, so we'd better prepare the two-day pictures as well.

Dark Theme

New features for Android10- Dark Theme , similar to app skin exchange

Basic Cognition

The dark theme features the following advantages

  • Can significantly reduce power consumption (depending on the device's screen technology) - For this reason, I personally feel that dark tones themselves have the ability to reduce power consumption
  • It doesn't make sense to improve the visibility of amblyopia and light-sensitive users - in plain words
  • It doesn't make sense to make it easier for everyone to use the device in a darker light environment - to put it in plain words

In Android 10 (API level 29) and later, there are three ways to enable a dark theme background

Note: Unlike in domestic and foreign markets, there are many manufacturers in China, so the following settings may not be supported

  • Enable dark theme background using system settings (Settings -> Display -> Theme).
  • Use the Shortcut Settings block to switch the theme background from the notification tray (when enabled).
  • On the Pixel device, choosing Power Saving Mode will also enable the dark theme background. Other OEMs do not necessarily support this behavior.

Change in-app theme background

For AndroidQ versions, the default theme background is slightly different

When the app is running on a device with Android 9 or lower, the recommended theme background options are:

  • Light colour
  • Dark
  • Set by power saving mode (recommended default option)

When running applications on Android 10 (API level 29) and later versions, the recommended options vary to allow users to replace the system defaults:

  • Light colour
  • Dark
  • System Default (recommended default option)

Note that if the user chooses Light, the power saving mode will not change this setting.

Each option maps directly to one of the following AppCompats. DayNight mode:

  • Light - MODE_NIGHT_NO
  • Dark - MODE_NIGHT_YES
  • Set by Power Saving Mode - MODE_NIGHT_AUTO_BATTERY
  • System Default - MODE_NIGHT_FOLLOW_SYSTEM
Notes for Use

About the sdk versions in my demo

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.example.stylemode"
        minSdkVersion 16
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

Since it's higher than Android 10, the themes resources needed for dark themes have been generated (for colors, you can share one colors and distinguish the colors of different themes by yourself)

themes

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.StyleMode" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>
</resources>

themes(night)

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.StyleMode" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_200</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/black</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_200</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>
</resources>

Note: Resource Adaptation

  • Color resources: Create a new values-night folder to replace all color values used in the page with those in dark mode.

  • Picture resources: Create a new mipmap-night/drawable-night folder, replacing both picture and style resources used in the page with corresponding resources in dark mode.

  • Status bar: Determine what color status bar is displayed by isDarkMode, the last method in the code above. It's best to operate in Base Activity, otherwise it's cumbersome to have a lot of activities.

Note: Call Adaptation

Scenarios are different, use slightly different, mainly because after theme switching, activity needs to call the recreate method

  • androidx

Can be called directly because setDefaultNightMode actively calls the apply method to rebuild the Activity

Actitiy is not rebuilt, so you need to call the activity yourself. Recreate() method

Basic Use

The official documentation already explains the call method

 AppCompatDelegate.setDefaultNightMode()

Because setDefaultNightModel needs to pass in mode (status), let's look at the method implementation of setDefaultNightModel (Friendly Tip: For the exact meaning of this enumerated class, the lines above have already explained that if you can't find it, you Ctrl + F...)

Interested in seeing AppCompatDelegate's methods for yourself, or quite a few...

Officials also provide a way to check the current theme, just look at it

int currentNightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) {
    case Configuration.UI_MODE_NIGHT_NO:
        // Night mode is not active, we're using the light theme
        break;
    case Configuration.UI_MODE_NIGHT_YES:
        // Night mode is active, we're using dark theme
        break;
}

How to use androidx

package com.example.stylemode;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
		
		//Switch between day mode and night mode
        findViewById(R.id.cut).setOnClickListener(v -> {
            if (AppCompatDelegate.getDefaultNightMode()<=1){
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
            }else{
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
            }
        });
    }
}

How to use support

I am too lazy to write, I recommend others directly.

public class MainActivity extends AppCompatActivity {

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

    public void click(View v){
        int mode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
        if (mode == Configuration.UI_MODE_NIGHT_YES) {
            AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
        } else {
            AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
        }
        getWindow().setWindowAnimations(R.style.WindowAnimationFadeInOut);
        //Difference
        recreate();
    }
}
Tool implementation

There are many tools on the web that I can learn from Here Of

DarkModeUtils

At a glance, after each mode switch, the enumerated values of the modes are stored in sp, which makes it a little easier to judge the modes... This can also be judged by the enumeration value, just be happy

package com.example.stylemode;

import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;

import androidx.appcompat.app.AppCompatDelegate;

public class DarkModeUtils {

    public static final String KEY_CURRENT_MODEL = "night_mode_state_sp";

    private static int getNightModel(Context context) {
        SharedPreferences sp = context.getSharedPreferences(KEY_CURRENT_MODEL, Context.MODE_PRIVATE);
        return sp.getInt(KEY_CURRENT_MODEL, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
    }

    public static void setNightModel(Context context, int nightMode) {
        SharedPreferences sp = context.getSharedPreferences(KEY_CURRENT_MODEL, Context.MODE_PRIVATE);
        sp.edit().putInt(KEY_CURRENT_MODEL, nightMode).apply();
    }

    /**
     * ths method should be called in Application onCreate method
     *
     * @param application application
     */
    public static void init(Application application) {
        int nightMode = getNightModel(application);
        AppCompatDelegate.setDefaultNightMode(nightMode);
    }

    /**
     * Apply Night Mode
     */
    public static void applyNightMode(Context context) {
        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
        setNightModel(context, AppCompatDelegate.MODE_NIGHT_YES);
    }

    /**
     * Apply daytime mode
     */
    public static void applyDayMode(Context context) {
        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
        setNightModel(context, AppCompatDelegate.MODE_NIGHT_NO);
    }

    /**
     * Dynamic switching required to follow system theme
     */
    public static void applySystemMode(Context context) {
        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
        setNightModel(context, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
    }

    /**
     * Determine if App is currently in dark mode
     *
     * @param context context
     * @return Return
     */
    public static boolean isDarkMode(Context context) {
        int nightMode = getNightModel(context);
        if (nightMode == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) {
            int applicationUiMode = context.getResources().getConfiguration().uiMode;
            int systemMode = applicationUiMode & Configuration.UI_MODE_NIGHT_MASK;
            return systemMode == Configuration.UI_MODE_NIGHT_YES;
        } else {
            return nightMode == AppCompatDelegate.MODE_NIGHT_YES;
        }
    }

}

Usage

package com.example.stylemode;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

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

        findViewById(R.id.cut).setOnClickListener(v -> {
            //Simple example, just look at the tool class if you need it
            DarkModeUtils.applyNightMode(MainActivity.this);
        });
    }
}

Topics: Android