在android 10以上,手机可开启深色模式,为了让手机更符合谷歌的规范,我们需要进行深色模式的适配
深色模式下,部分手机系统开启深色模式后,会自动帮我们将app转变为深色模式,即使我们并没有进行适配;这种自动的转色,大部分时候不尽人意,为了不让app在深色模式下显示紊乱,我们需要关闭深色模式;
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" ... />
适配深色模式一般有以下几种方式:
我们采用第二种方案来讲解,完全自己适配,这样讲的东西比较多一点;
第一步我们要操作的当然是关闭自动深色模式,让我们自己接手颜色适配,参照文章中的第一个步骤即可;
当系统转变为深色模式时,会自动调用res目录下的xxx-night目录,在其中寻找匹配的颜色或图片;
1.我们需要在res目录下,创建values-night文件夹,在文件夹中,创建colors.xml文件,用以适配颜色;
其中,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>
1.我们可能在深色模式下,需要图标展现不同的颜色,如亮色模式下,图标是黑色的, 而在深色模式下,图标需要是白色的,我们只需创建drawable-night / mipmap-night 即可,如下:
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" />
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内设置切换深色模式,对此,我们可能需要手动切换为深色模式,即使系统当前并非深色模式;
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; }