Android Activity切换深色/夜间主题前快速闪白解决方案咨询
根本原因
你看到的闪白是Android系统的窗口预绘制机制导致的:执行super.onCreate(savedInstanceState)时,系统会直接使用AndroidManifest中为该Activity配置的默认主题初始化窗口背景,此时你还没调用夜间模式切换逻辑,默认主题如果是浅色的,就会先绘制一层白色背景,等你后续调用updateForTheme切换到深色模式后窗口重绘,就出现了闪白。
可行解决方案
方案1:配置启动占位主题(推荐,改造成本最低)
给所有需要支持深色模式的Activity配置一套和业务主题背景一致的占位启动主题,提前适配深色模式,从根源上避免预绘制的背景和实际主题不一致。
- 首先在
res/values/styles.xml和res/values-night/styles.xml中分别定义占位主题:res/values/styles.xml(浅色模式)
<style name="Theme.MyApp.StartUp" parent="Theme.MyApp"> <item name="android:windowBackground">@color/white</item> <!-- 和你主界面的背景色一致 --> <item name="android:statusBarColor">@color/white</item> </style>
res/values-night/styles.xml(深色模式)
<style name="Theme.MyApp.StartUp" parent="Theme.MyApp"> <item name="android:windowBackground">@color/black</item> <!-- 和你深色模式主界面背景色一致 --> <item name="android:statusBarColor">@color/black</item> </style>
- 在AndroidManifest中给Activity应用这个占位主题:
<activity android:name=".ui.settings.SettingsActivity" android:theme="@style/Theme.MyApp.StartUp" /> <activity android:name=".ui.main.MainActivity" android:theme="@style/Theme.MyApp.StartUp" />
- 最后在Activity的
setContentView之前把windowBackground重置,避免不必要的过度绘制:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) updateForTheme(Theme.DARK) // 重置windowBackground window.setBackgroundDrawable(null) setContentView(R.layout.activity_settings) }
方案2:拆分主题存储逻辑,提前切换主题
不要把主题配置的读取依赖ViewModel(ViewModel本身就是在super.onCreate之后才能初始化),把主题配置存储到SharedPreferences/DataStore中,在super.onCreate之前就能直接读取。
因为Activity执行到onCreate时Context已经初始化完成,可以直接读取本地存储,不需要等待Dagger注入完成:
override fun onCreate(savedInstanceState: Bundle?) { // 直接从本地存储读主题配置,不需要依赖Dagger注入的ViewModel val savedTheme = getSharedPreferences("app_config", MODE_PRIVATE).getInt("theme", Theme.SYSTEM.ordinal) updateForTheme(Theme.values()[savedTheme]) super.onCreate(savedInstanceState) // 后续Dagger注入、初始化ViewModel逻辑 setContentView(R.layout.activity_settings) }
如果你用DataStore存储主题,可以在Application层提前初始化一个全局可访问的DataStore单例,不需要依赖Activity级别的Dagger组件即可读取。
方案3:在attachBaseContext中处理主题切换
attachBaseContext的执行时机比onCreate更早,你可以重写这个方法提前完成主题切换,完全避免预绘制冲突:
override fun attachBaseContext(newBase: Context?) { // 提前读取主题配置切换 val savedTheme = newBase?.getSharedPreferences("app_config", MODE_PRIVATE)?.getInt("theme", Theme.SYSTEM.ordinal) ?: Theme.SYSTEM.ordinal updateForTheme(Theme.values()[savedTheme]) super.attachBaseContext(newBase) }
内容的提问来源于stack exchange,提问作者Jim Clermonts
相关产品推荐
相关产品推荐

