Loading...

android 黑夜模式适配记录


在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内设置切换深色模式,对此,我们可能需要手动切换为深色模式,即使系统当前并非深色模式;

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