【Android】日夜间模式填坑记

简单使用

设置夜间模式的类是AppCompatDelegate。

设置全局夜间模式

public static void setDefaultNightMode(@NightMode int mode) {

给页面单独设置夜间模式

@RequiresApi(17)
public abstract void setLocalNightMode(@NightMode int mode);

在Activity中调用,注意,要在setContentView之前:

getDelegate.setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES)

配置夜间模式资源

在资源文件夹中创建对应的夜间模式文件夹,后缀为-night。例如:values文件夹下的colors.xml,此时我们想给 <color name="bg">#FFFFFFFF</color>,配置对应的夜间模式色值,先创建values-night文件夹,然后在文件夹下创建文件colors.xml,在xml文件中写入<color name="bg">#FF000000</color>。此时夜间模式的资源就配置好了,在布局文件中使用即可,系统会根据代码的设置,读取对应的色值。

关于页面重建recreate

当我们给页面单独设置模式时,Activity会调用recreate方法,重新创建页面。重建之前,Activity的生命周期也会执行一遍。

private boolean updateForNightMode(@ApplyableNightMode final int mode,
            final boolean allowRecreation) {
        boolean handled = false;

        final Configuration overrideConfig =
                createOverrideConfigurationForDayNight(mContext, mode, null);

        final boolean activityHandlingUiMode = isActivityManifestHandlingUiMode();
        final int currentNightMode = mContext.getResources().getConfiguration().uiMode
                & Configuration.UI_MODE_NIGHT_MASK;
        final int newNightMode = overrideConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;

        if (currentNightMode != newNightMode
                && allowRecreation
                && !activityHandlingUiMode
                && mBaseContextAttached
                && (sCanReturnDifferentContext || mCreated)
                && mHost instanceof Activity
                && !((Activity) mHost).isChild()) {
//			调用重建
            ActivityCompat.recreate((Activity) mHost);
            handled = true;
        }

        if (!handled && currentNightMode != newNightMode) {
            updateResourcesConfigurationForNightMode(newNightMode, activityHandlingUiMode, null);
            handled = true;
        }

        if (handled && mHost instanceof AppCompatActivity) {
            ((AppCompatActivity) mHost).onNightModeChanged(mode);
        }

        return handled;
    }

因此,我们看到页面出现后马上消失了,然后再次出现,会闪一下。为了避免这种情况,我们可以在Manifest中为Activity配置configChanges

<activity
    android:configChanges="uiMode"
/>

弹窗类Activity导致的前一个Activity颜色混乱

有时候我们会启动这样一个Acitivty,UI效果类似于一个dialog,并且需要在代码中单独设置了夜间模式。
一般我们会这样给Activity设置主题:

<activity
    android:theme="@style/dialog_activity" 
/>
<style name="dialog_activity" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="windowActionBar">false</item>
    <item name="android:windowNoTitle">true</item>
    <item name="colorPrimaryDark">@android:color/transparent</item>
    <!--设置动画,在这里使用让它继承系统的Animation.Dialog-->
    <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
    <item name="android:windowBackground">@android:color/transparent</item>
    <!--背景透明-->
    <item name="android:backgroundDimEnabled">true</item>
    <!-- 半透明 -->
    <item name="android:windowIsTranslucent">true</item>
</style>

当关闭掉弹窗,回到前一个Activity时,Activity的部分UI会使用夜间模式,UI中存在RecyclerView时,比较容易复现。

目前不清楚出现这种问题的原因是什么,以下仅为猜测。因为window的背景是透明的,前一个Activity的部分UI对用户可见,那么当前一个Acitivty使用日间模式,dialog使用夜间模式时,为了保证用户的体验一致,即dialog和背景Activity都使用夜间模式,系统会暂时给背景Activity设置夜间模式。测试中,只要背景不透明,看不到背景Activity的UI,就不会发生颜色混乱。

解决办法

尝试了多种办法后,最终放弃了给弹窗类Activity单独设置夜间模式。取而代之,使用了主题色。

自定义一部分属性:

    <attr name="dialog_st_text_lv1" format="color|reference"/>
    <attr name="dialog_main_bg" format="color|reference"/>
    <attr name="dialog_title_color" format="color|reference"/>
    <attr name="dialog_title_bg" format="reference"/>
    <attr name="dialog_edit_bg" format="reference"/>
    <attr name="dialig_st_divider_lv1" format="reference|color"/>

然后添加日间模式主题,设置对应的资源:

 <style name="dialog_activity.light">
     <item name="dialog_main_bg">#ffffff</item>
     <item name="dialog_st_text_lv1">#ff111111</item>
     <item name="dialog_title_color">#333333</item>
     <item name="dialog_title_bg">@drawable/bg_dialog_title_light</item>
     <item name="dialog_edit_bg">@drawable/edittext_bg_light</item>
     <item name="dialig_st_divider_lv1">#fff2f2f2</item>
 </style>

添加夜间模式主题,设置对应的资源:

<style name="dialog_activity.dark">
    <item name="dialog_main_bg">#191b27</item>
    <item name="dialog_st_text_lv1">#ffe4e4e4</item>
    <item name="dialog_title_color">#cfd1dc</item>
    <item name="dialog_title_bg">@drawable/bg_dialog_title_dark</item>
    <item name="dialog_edit_bg">@drawable/edittext_bg_dark</item>
    <item name="dialig_st_divider_lv1">#ff07080b</item>
</style>

最后在layout中调用:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="wrap_content"
    tools:theme="@style/dialog_activity.light">

     <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/dialog_title_bg">
     </FrameLayout>

</RelativeLayout>

不要忘记在代码中设置主题:

@Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {Bundle bundle = getIntent().getExtras();
      if (IS_NIGHT_MODE) {
          setTheme(R.style.dialog_activity_light);
      }else {
          setTheme(R.style.dialog_activity_dark);
      }
     super.onCreate(savedInstanceState);
 }

其他疑问

当一个弹窗类Activity,没有设置android:configChanges="uiMode",理所当然会进行recreate,但是仅限于第一次出现。如果再次弹窗就不会recreate,如果前一个Activity执行了onPause,再弹窗还是会出现一次recreate,不知道是不是有缓存

参考:

AppCompatDelegate API

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值