return when (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
Configuration.UI_MODE_NIGHT_YES -> true
else -> false
}
}
Tips: 对于一些从网络接口服务获取的需要对深色模式区分的色值或者图片,可以使用上述的判断来获取对应的资源。
通过AppCompatDelegate.getDefaultNightMode())可以获取五种深色模式场景:
-
MODE_NIGHT_AUTO_BATTERY 低电量模式自动开启深色模式
-
MODE_NIGHT_FOLLOW_SYSTEM 跟随系统开启和关闭深色模式(默认)
-
MODE_NIGHT_NO 强制使用notnight资源,表示非深色模式
-
MODE_NIGHT_YES 强制使用night资源
-
MODE_NIGHT_UNSPECIFIED 配合 setLocalNightMode(int)) 使用,表示由Activity通过AppCompactActivity.getDelegate()来单独设置页面的深色模式,不设置全局模式
深色模式设置可以从三个层级设置,分别是系统层、Applcation层以及Activity层。底层的设置会覆盖上层的设置,例如系统设置了深色模式,但是Application设置了浅色模式,那么应用会显示浅色主题。
系统层是指系统设置中,根据不同产商的手机,可以在设置->显示中修改系统为深色模式。
Application层通过AppCompatDelegate.setDefaultNightMode()设置深色模式。
Activity层通过getDelegate().setLocalNightMode())设置深色模式。
当深色模式改变时,Activity会重建,如果不希望Activity重建,可以在AndroidManifest.xml中对对应的Activity设置android:configChanges=“uiMode”,不过设置之后页面的颜色改变需要Activity在中通过监听onConfigurationChanged来动态改变。
通过AppCompatDelegate.setDefaultNightMode(int))可以设置深色模式,源码如下:
public static void setDefaultNightMode(@NightMode int mode) {
if (DEBUG) {
Log.d(TAG, String.format(“setDefaultNightMode. New:%d, Current:%d”,
mode, sDefaultNightMode));
}
switch (mode) {
case MODE_NIGHT_NO:
case MODE_NIGHT_YES:
case MODE_NIGHT_FOLLOW_SYSTEM:
case MODE_NIGHT_AUTO_TIME:
case MODE_NIGHT_AUTO_BATTERY:
if (sDefaultNightMode != mode) {
sDefaultNightMode = mode;
applyDayNightToActiveDelegates();
}
break;
default:
Log.d(TAG, “setDefaultNightMode() called with an unknown mode”);
break;
}
}
从源码可以看出设置 MODE_NIGHT_UNSPECIFIED 模式是不会生效的。
Tips:注意,深色模式变化会导致Activity重建。
自定义适配
1. 主题
将Application和Activity的主题修改为集成自Theme.AppCompat.DayNight或者Theme.MaterialComponents.DayNight,就可以对于大部分的控件得到较好的深色模式支持。我们看下DayNight主题的定义:
res/values/values.xml
<?xml version="1.0" encoding="utf-8"?>res/values-night/themes.xml
Tips: 若需要动态修改主题要在调用inflate之前调用,否则不会生效。
2. 色值
主题切换颜色
除了定义不同模式使用不同的主题,我们还可以对主题设置自定义的色值。在设置主题色值之前,我们先了解一下Android主题的颜色系统。
-
colorPrimary:主要品牌颜色,一般用于ActionBar背景
-
colorPrimaryDark:默认用于顶部状态栏和底部导航栏
-
colorPrimaryVariant:主要品牌颜色的可选颜色
-
colorSecondary:第二品牌颜色
-
colorSecondaryVariant:第二品牌颜色的可选颜色
-
colorPrimarySurface:对应Light主题指向colorPrimary,Dark主题指向colorSurface
-
colorOn[Primary, Secondary, Surface …],在Primary等这些背景的上面内容的颜色,例如ActioBar上面的文字颜色
-
colorAccent:默认设置给colorControlActivated,一般是主要品牌颜色的明亮版本补充
-
colorControlNormal:图标和控制项的正常状态颜色
-
colorControlActivated:图标和控制项的选中颜色(例如Checked或者Switcher)
-
colorControlHighlight:点击高亮效果(ripple或者selector)
-
colorButtonNormal:按钮默认状态颜色
-
colorSurface:cards, sheets, menus等控件的背景颜色
-
colorBackground:页面的背景颜色
-
colorError:展示错误的颜色
-
textColorPrimary:主要文字颜色
-
textColorSecondary:可选文字颜色
Tips: 当某个属性同时可以通过 ?attr/xxx 或者?android:attr/xxx获取时,最好使用?attr/xxx,因为?android:attr/xxx是通过系统获取,而?attr/xxx是通过静态库类似于AppCompat 或者 Material Design Component引入的。使用非系统版本的属性可以提高平台通用性。
如果需要自定义主题颜色,我们可以对颜色分别定义notnight和night两份,放在values以及values-night资源文件夹中,并在自定义主题时,传入给对应的颜色属性。例如:
res/values/styles.xml
res/values/colors.xml
<?xml version="1.0" encoding="utf-8"?>#4D71FF
#FFFFFF
#101214
#E0A62E
res/values-night/colors.xml
<?xml version="1.0" encoding="utf-8"?>#FF584D
#0B0C0D
#F5F7FA
#626469
控件切换颜色
同样的,我们可以在布局的XML文件中直接使用定义好的颜色值,例如
<TextView
android:id="@+id/auto_color_text"
android:text=“自定义变色文字”