调用requireActivity().recreate()重启Activity时如何避免短暂黑屏?
更改Locale后调用
recreate()出现黑屏的解决方案 黑屏的根本原因是recreate()执行时,系统销毁旧Activity、创建新Activity的过渡间隙,会默认显示当前Activity主题设置的windowBackground属性值,未自定义该属性时通常会显示系统默认的黑色背景,也就是你看到的0.3秒黑屏。以下是3种可行的实现方案,可根据你的项目情况选择:
方案1:自定义过渡窗口背景(成本最低,推荐优先尝试)
直接修改主题的windowBackground属性,让过渡间隙显示你需要的闪屏/进度条占位图,不需要修改业务逻辑:
- 第一步:在
res/drawable目录下创建占位图drawable,示例如下:
<!-- res/drawable/locale_switch_placeholder.xml --> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 背景色可替换为你的产品主色 --> <item android:drawable="@color/white" /> <!-- 中间显示进度条,也可替换为产品logo等自定义内容 --> <item android:gravity="center"> <rotate android:fromDegrees="0" android:toDegrees="360"> <shape android:shape="oval"> <size android:width="32dp" android:height="32dp" /> <stroke android:width="3dp" android:color="@color/your_primary_color" /> </shape> </rotate> </item> </layer-list>
- 第二步:给目标Activity的主题添加
windowBackground配置:
<style name="YourActivityTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar"> <!-- 其他原有主题配置 --> <item name="android:windowBackground">@drawable/locale_switch_placeholder</item> <!-- 可选:关闭默认Activity过渡动画,避免闪烁 --> <item name="android:windowAnimationStyle">@null</item> </style>
- 第三步:在Activity的
onCreate方法中恢复正常窗口背景,避免占位图影响日常页面显示:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 恢复为普通页面的窗口背景,可根据你的业务需求设置 window.setBackgroundDrawableResource(R.color.white) setContentView(R.layout.your_activity_layout) // 其他原有业务逻辑 }
方案2:手动刷新UI避免Activity重建(完全无黑屏,推荐性能要求高的场景)
不调用系统的recreate()方法,Locale更改后手动刷新当前页面所有文本资源,从根源上避免销毁重建的过渡过程:
- 更改Locale配置后先更新应用全局资源配置:
fun updateAppLocale(newLocale: Locale) { val resources = requireContext().resources val config = resources.configuration config.setLocale(newLocale) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { config.setLocales(LocaleList(newLocale)) } resources.updateConfiguration(config, resources.displayMetrics) }
- 手动更新当前页面所有View的文本内容:
fun refreshUIAfterLocaleChange() { // 所有文本资源重新从strings.xml取值赋值 binding.titleTv.setText(R.string.page_title) binding.confirmBtn.setText(R.string.confirm) // 刷新列表、Fragment等组件 contentAdapter.notifyDataSetChanged() childFragmentList.forEach { it.refreshLocaleText() } }
如果你的项目使用了ViewModel+LiveData/Flow,可以将Locale状态作为全局可监听数据,所有文本通过监听Locale状态自动更新,不需要手动逐个赋值。
方案3:重建前添加全屏覆盖层(适合必须使用recreate()的场景)
如果你的业务逻辑依赖Activity重建,无法使用方案2,可以在触发重建前先显示一个全屏的覆盖层展示闪屏/进度条,重建完成后再隐藏:
- 触发Locale切换时先显示全屏无动画Dialog:
fun switchLocale(newLocale: Locale) { // 创建全屏进度Dialog val progressDialog = Dialog(requireContext(), android.R.style.Theme_Black_NoTitleBar_Fullscreen) progressDialog.setContentView(R.layout.your_splash_progress_layout) progressDialog.setCancelable(false) progressDialog.window?.setDimAmount(0f) progressDialog.show() // 全局存储重建标记,可放在Application类或者静态变量中 MyApp.isLocaleRebuilding = true // 存储Dialog实例,或者在重建后重新获取 MyApp.tempProgressDialog = progressDialog requireActivity().recreate() }
- Activity重建完成后隐藏覆盖层:
override fun onResume() { super.onResume() if (MyApp.isLocaleRebuilding) { // 可选加100ms延迟,避免页面未渲染完成出现闪烁 Handler(Looper.getMainLooper()).postDelayed({ MyApp.tempProgressDialog?.dismiss() MyApp.tempProgressDialog = null }, 100) MyApp.isLocaleRebuilding = false } }
内容的提问来源于stack exchange,提问作者sooyeon
相关产品推荐
相关产品推荐

