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); } }); } }
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); }); } }