Android Studio Java开发 深浅色模式切换Toggle方法运行异常
问题成因
这个bug是Android深色模式切换时的典型状态不一致问题,核心原因有三个:
- 调用
AppCompatDelegate.setDefaultNightMode()切换主题时,所有在前台的Activity都会被系统强制重建来加载新主题资源。你定义的isDarkMode是Activity的普通成员变量,会随Activity销毁重建被重置,你在点击事件里给旧Activity实例的isDarkMode赋值,对重建后的新实例完全没有作用。 - 你在
onCreate里直接读取Configuration.uiMode判断当前主题的方式不可靠:在部分Android版本、厂商定制ROM上,Activity刚完成重建的时间点,Configuration对象的uiMode字段可能还没同步更新完成,会读到旧的主题值,直接导致isDarkMode赋值错误。 - 点击事件里手动修改按钮图标、手动修改
isDarkMode值属于冗余逻辑:Activity重建后onCreate会重新执行初始化流程,自动给变量赋值、设置正确的按钮图标,你在即将销毁的旧Activity实例上修改UI和变量,只会在旧界面销毁前短暂生效,反而会干扰新实例的状态初始化,引发值错乱。
你遇到的「深色模式下第二次点击按钮isDarkMode异常变false」,就是旧实例的赋值操作和新实例初始化时读到的未更新Configuration值冲突导致的。
修复方案
直接按照下面的逻辑调整代码即可:
- 删除原有
isDarkMode成员变量,不需要额外维护主题状态,全局主题统一以AppCompatDelegate.getDefaultNightMode()返回的值为准,避免多份状态存储导致的不一致。 - 删除点击事件里手动修改按钮图标、手动给变量赋值的代码,点击事件里只需要调用
AppCompatDelegate.setDefaultNightMode()完成主题切换即可,后续UI和变量初始化交给重建后的onCreate处理。 - 把
onCreate里判断当前主题的逻辑,从读Configuration.uiMode改成优先读AppCompatDelegate.getDefaultNightMode(),这个值是AppCompat框架内部维护的主题状态,不会出现更新延迟的问题,判断结果更可靠。 - (可选)如果需要让用户选择的主题在应用重启后依然生效,可以把主题选择结果存到
SharedPreferences,在Application类的onCreate方法里提前读取存储的值,调用setDefaultNightMode初始化全局主题。
修复后的可运行代码如下:
ImageButton dark_light; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); dark_light = findViewById(R.id.dark_light); // 优先读取AppCompat维护的当前主题模式,避免Configuration同步延迟问题 int currentNightMode = AppCompatDelegate.getDefaultNightMode(); // 首次启动未手动设置过主题时,默认读取系统当前主题 if (currentNightMode == AppCompatDelegate.MODE_NIGHT_UNSPECIFIED) { currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; } // 根据当前主题设置正确的按钮图标 if (currentNightMode == AppCompatDelegate.MODE_NIGHT_YES || currentNightMode == Configuration.UI_MODE_NIGHT_YES) { dark_light.setImageResource(R.drawable.light); } else { dark_light.setImageResource(R.drawable.dark); } dark_light.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int currentMode = AppCompatDelegate.getDefaultNightMode(); if (currentMode == AppCompatDelegate.MODE_NIGHT_UNSPECIFIED) { currentMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; } // 仅做主题切换,不需要额外手动修改UI或变量 if (currentMode == AppCompatDelegate.MODE_NIGHT_YES || currentMode == Configuration.UI_MODE_NIGHT_YES) { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); } else { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); } } }); }
如果不想让Activity在切换主题时重建,可以在AndroidManifest.xml对应Activity的节点下添加android:configChanges="uiMode|nightMode"配置,然后重写Activity的onConfigurationChanged方法,在方法里手动更新按钮图标即可,不过这种方式需要手动处理所有和主题相关的资源加载,兼容性不如默认重建方案,非必要不推荐使用。
内容的提问来源于stack exchange,提问作者user17765346
相关产品推荐
相关产品推荐

