android 黑夜模式适配记录

2022年12月24日12:29:35

前言

在android 10以上,手机可开启深色模式,为了让手机更符合谷歌的规范,我们需要进行深色模式的适配


一、不开启深色模式

深色模式下,部分手机系统开启深色模式后,会自动帮我们将app转变为深色模式,即使我们并没有进行适配;这种自动的转色,大部分时候不尽人意,为了不让app在深色模式下显示紊乱,我们需要关闭深色模式;

1.关闭自动深色模式

1.一般我们都会让整个app使用统一的theme,如下:
<application
	...
	android:theme="@style/mTheme">
	...
</application>
在style.xml中,定义我们的theme,如下:
<style name="mTheme" parent="Theme.AppCompat.DayNight.NoActionBar">

        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:windowAnimationStyle">@style/default_animation</item>
        <item name="android:windowBackground">@color/base_color_FDFDFD</item>
    </style>
为了关闭部分手机系统自动将app转为深色模式,我们需要在style中加入如下一行:
加入之后一般会报错,提示为此设置为api 29 以上才有,我们只需根据提示快速生成value-v29文件夹,
此时会自动在value-v29文件夹下生成style.xml
<item name="android:forceDarkAllowed">false</item>
2.如果APP在前台,系统转为深色模式,APP可见的Activity将会被重新创建,既然我们不开启深色模式,
那么我们也不需要重新创建Activity,在 AndroidManifest.xml 中,我们对Activity 做如下修改:
<activity
            ...
            android:configChanges="uiMode"
			...
/>

二、自己适配深色模式

适配深色模式一般有以下几种方式:

  • 让系统自动适配;就是上面我们关闭的那种,大部分情况下自动转变深色模式不尽人意,部分手机也无法实现;
  • 完全由我们自己适配;即为每种颜色定义一个深色模式下的颜色值,这种方案也需要关闭系统自动转变深色模式的功能,否则部分深色模式下的颜色值不生效,这种方案工作量比较大;
  • 系统自动适配与我们自己的适配共同使用;这种方案工作量相对较小;系统自动适配颜色,对转换的不如意的View,我们可以 setForceDarkAllowed(false),然后再自己适配颜色;

我们采用第二种方案来讲解,完全自己适配,这样讲的东西比较多一点;

1.关闭自动深色模式

第一步我们要操作的当然是关闭自动深色模式,让我们自己接手颜色适配,参照文章中的第一个步骤即可;

2.创建res深色适配目录

当系统转变为深色模式时,会自动调用res目录下的xxx-night目录,在其中寻找匹配的颜色或图片;

(1) 适配colors,strings
1.我们需要在res目录下,创建values-night文件夹,在文件夹中,创建colors.xml文件,用以适配颜色;

android 黑夜模式适配记录
其中,values 下 colors.xml 内的颜色值名称 与 values-night 下 colors.xml 内的颜色值名称必须相同,这样,当转变为深色模式时,系统将使用 values-night 下的 colors.xml,如果 values-night 下没有相同的颜色值名称,则会继续使用 values 下colors.xml 内的颜色值;

2.strings 的适配也与上同理,如果我们在string中使用了富文本,
只需要在values-night 下生成strings.xml 即可;如下:
values 下:
<string name="fuWenBen">
        <![CDATA[
           <font><textFont color=\'#000000\' size='41px'>%s</textFont></font><br/>谁看过我
         ]]>
</string>

values-night 下:
 <string name="fuWenBen">
        <![CDATA[
           <font><textFont color=\'#FFFFFF\' size='41px'>%s</textFont></font><br/>谁看过我
         ]]>
 </string>
(2) 适配图片或者shape等
1.我们可能在深色模式下,需要图标展现不同的颜色,如亮色模式下,图标是黑色的,
而在深色模式下,图标需要是白色的,我们只需创建drawable-night / mipmap-night 即可,如下:

android 黑夜模式适配记录

2.其实适配图标我们还有其他的办法,即在colors中定义图标的颜色,如下:
values 下
<!--    图标颜色 灰色-白色-->
    <color name="iconColor_999999">#999999</color>
values-night 下
 <!--    图标颜色 灰色-白色-->
    <color name="iconColor_999999">#FFFFFF</color>
定义完成之后,我们在需要变色的imageView,定义tint即可,tint 会帮助我们将图标修改成
我们定义的颜色,如下:
<ImageView
			...
			android:src="@drawable/icon_phone"
 			app:tint="@color/iconColor_999999"
            tools:ignore="ContentDescription" />
动态设置ImageView的tint,如下:
imageView.setImageDrawable(ContextCompat.getDrawable(context,R.drawable.icon_phone));
imageView.setImageTintList(
	ColorStateList.valueOf(
		ContextCompat.getColor(context, R.color.colorBlack)
	)
);
3.给亮色的背景图片增加蒙版,而不用增加图片来适配,如下:
values 下
<!--    图标颜色 灰色-白色-->
    <color name="iconColor_mark">#00000000</color>
values-night 下
 <!--    图标颜色 灰色-白色-->
    <color name="iconColor_mark">#40000000</color>
定义完成后,我们在需要增加蒙版的ImageView 增加 tintMode 与 tint,如下:
<ImageView
			...
            android:src="@drawable/pink_bg"
            android:scaleType="fitXY"
            android:tintMode="src_atop"
            app:tint="@color/iconColor_mark"
            tools:ignore="ContentDescription" />

<Button
		...
		android:background="@drawable/pink_bg"
		android:backgroundTintMode="src_atop"
        android:backgroundTint="@color/iconColor_mark"
       />         

(3) Activity的处理
1.当我们完成以上适配后,就需要注意一点,当APP处于前台,系统转为深色模式后,
activity会被重新创建并切换深色模式,某些情况下这并不是我们希望看到的,
所以当系统转为深色模式时,如果我们不需要让activity重新创建,我们需要做如下编码:
<activity
            ...
            android:configChanges="uiMode"
			...
/>
不被重新创建的Activity,将不会应用深色模式,此种情况下,我们可能需要自己处理一些深色转变,
我们在Activity做如下编码,当系统深色模式启用/关闭时,以下方法将会被调用:
 	@Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        int currentNightMode = newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
        if (currentNightMode == Configuration.UI_MODE_NIGHT_YES){
			//系统开启了深色模式,自己做相应处理
		}else {
            //系统关闭了深色模式,自己做相应处理
        }
    }

三、APP内开启深色模式

有时候我们可能会有需求,在APP内设置切换深色模式,对此,我们可能需要手动切换为深色模式,即使系统当前并非深色模式;

1.做如下编码切换APP为 深色/亮色 模式:
	(1).已经被创建的Activity 只有调用recreate() / 关闭重新创建 才能切换深色/亮色模式;
	(2).调用此方法不会触发回调 onConfigurationChanged() 方法;
//设置为日间亮色模式
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
//设置为夜间深色模式
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
//重新创建Activity,以使用 深色/亮色 模式
recreate();
2.做如下编码切换当前Activity为 深色/亮色 模式:
	(1).此方法不会使除 当前调用处的Activity 外 已创建/未创建 的 Activity 转换模式,简而言之,
	只有当前的Activity 会切换模式;
	(2).如果在AndroidManifest.xml中配置当前Activity android:configChanges="uiMode",则不会
	切换模式,但是会回调onConfigurationChanged() 方法;可调用reCreate()使当前Activity切换
	模式;
	(3).如果未配置configChanges="uiMode",则会重新创建当前Activity,并切换模式,不会回调
	onConfigurationChanged()方法;
//设置为日间亮色模式
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_NO);
//设置为夜间深色模式
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
3.判断当前的手机系统是否为深色模式:
	(1).此方法是判断系统的深色模式是否开启,我们上面的操作并不会影响此判断;
public boolean isDarkTheme() {
        int flag = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
        return flag == Configuration.UI_MODE_NIGHT_YES;
    }

  • 作者:帅到不敢打代码
  • 原文链接:https://blog.csdn.net/weixin_45379305/article/details/116042822
    更新时间:2022年12月24日12:29:35 ,共 4896 字。