1、Force Dark
一种能让应用程序快速适配深色主题,Force Dark的工作原理是系统会分析浅色主题应用下的每一层View,并且在这些View绘制到屏幕之前,自动将它们的颜色转换成更加适合深色主题的颜色。注意,只有原本使用浅色主题的应用才能使用这种方式,如果你的应用原本使用的就是深色主题,Force Dark将不会起作用。
右击res目录 -> New -> Directory,创建一个values-v29目录,然后右击values-v29目录 -> New -> Values resource file,创建一个styles.xml文件。接着对这个文件进行编写,代码如下所示:
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:forceDarkAllowed">true</item>
</style>
</resources>
除了android:forceDarkAllowed属性之外,其他的内容都是从之前的styles.xml文件中复制过来的。这里给AppTheme主题增加了android:forceDarkAllowed属性并设置为true,说明现在我们是允许系统使用Force Dark将应用强制转换成深色主题的。另外,values-v29目录是只有Android 10.0及以上的系统才会去读取的,因此这是一种系统差异型编程的实现方式。
2、values-night适配
(1)将AppTheme的parent主题指定成了Theme.AppCompat.DayNight.NoActionBar ,这是一种DayNight主题。因此,在普通情况下MaterialTest项目仍然会使用浅色主题,和之前并没有什么区别,但是一旦用户在系统设置中开启了深色主题,MaterialTest项目就会自动使用相应的深色主题
(2)创建values-night目录,增加color.xml
color.xml中的颜色命名与values目录下的color.xml中的命名相同,在普通情况下,系统仍然会读取values/colors.xml文件中的颜色值,而一旦用户开启了深色主题,系统就会去读取values-night/colors.xml文件中的颜色值了。
如果适配图片的话,就创建对应的 drawable-night目录
3、切换主题
黑暗模式和正常模式,无非就是两种主题间的切换(主要是各种背景色,字体颜色和Icon)。因此我们只需要定义两套不同的主题,根据是否是黑暗模式进行主题的切换即可
(1)判断当前是否处于黑暗模式:用于启动时还在不同的主题
//检查当前系统是否已开启暗黑模式
public static boolean getDarkModeStatus(Context context) {
int mode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
return mode == Configuration.UI_MODE_NIGHT_YES;
}
(2)定义两套主题(正常模式和黑暗模式):即在style文件下自定义两个style,但是必须指定parent为‘Theme.AppCompat.DayNight.DarkActionBar’,如下所示:
//正常模式下的主题
<style name="main_theme_light" parent="Theme.AppCompat.DayNight.DarkActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="main_text_color">@color/main_text_color_light</item>
<item name="main_bg_color">@color/main_bg_color_light</item>
</style>
//黑暗模式下的主题
<style name="main_theme_dark" parent="Theme.AppCompat.DayNight.DarkActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="main_text_color">@color/main_text_color_dark</item>
<item name="main_bg_color">@color/main_bg_color_dark</item>
</style>
(3)找出适配黑暗模式需要的属性(主要是颜色属性:背景色、字体颜色和Icon颜色等并给属性赋值),类似如下定义:
<!-- 主要字体颜色-->
<attr name="main_text_color" format="color" />
<!-- 主要背景颜色-->
<attr name="main_bg_color" format="color" />
//不同模式下的颜色属性值
<color name="main_text_color_light">#000000</color>
<color name="main_text_color_dark">#ffffff</color>
<color name="main_bg_color_light">#ffffff</color>
<color name="main_bg_color_dark">#000000</color>
(4)在activity和xml中引用我们自定义的属性:
//在xml文件中使用我们自定义属性
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/main_bg_color">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textColor="?attr/main_text_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
(5)在BaseActivity中切换不同的主题,才能使我们自定义的属性生效,必须在setContentView()方法前设置:
//在BaseActivity中切换不同的主题,才能使我们自定义的属性生效,必须在setContentView()方法前设置:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (getDarkModeStatus(this)) {
setTheme(R.style.main_theme_dark);
}else {
setTheme(R.style.main_theme_light);
}
setContentView(R.layout.activity_main)
}
//为达到更好的适配效果,可在xml文件的activity节点下加入如下属性:
android:configChanges="uiMode
tips:
1、监听深色主题是否开启
1)在清单文件中给对应的Activity配置 android:configChanges=“uiMode”
2)在onConfigurationChanged方法中获取:
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
int currentNightMode = newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) {
case Configuration.UI_MODE_NIGHT_NO:
Log.e("=======","=====关闭夜间模式====");
// 关闭
break;
case Configuration.UI_MODE_NIGHT_YES:
Log.e("=======","=====开启夜间模式====");
// 开启
break;
default:
break;
}
}
2、判断深色主题是否开启
public static boolean isNightMode(Context context) {
int currentNightMode = context.getResources().getConfiguration().uiMode &
Configuration.UI_MODE_NIGHT_MASK;
return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
}
3、可以通过Force Dark来自动适配,然后再通过values-night适配来进行图片资源以及颜色等的适配。