Activity重建或切换夜间模式时Edge-to-edge功能失效问题
切换主题后Edge-to-Edge失效,Insets监听不触发的解决方案
调用AppCompatDelegate.setDefaultNightMode切换日间/夜间主题后,Activity重建恢复状态,但Edge-to-edge功能失效,setOnApplyWindowInsetsListener未被触发。该问题在模拟器及部分三星设备上复现,Pixel 9设备正常。
相关代码
BaseActivity.kt 启用Edge-to-edge
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d(Const.TAG_ACTIVITY, String.format("%s BaseActivity onCreate()", this.javaClass.simpleName)) enableEdgeToEdge() initBinding() onActivityCreate(savedInstanceState) observeLiveData() }
InsetsManager.kt 设置Insets监听
object InsetsManager { fun applyBottomNavigationViewInsets(bottomContainerView: ViewGroup) { ViewCompat.setOnApplyWindowInsetsListener(bottomContainerView) { v, windowInsets -> val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) v.setPadding(insets.left, insets.top, insets.right, insets.bottom) WindowInsetsCompat.CONSUMED } } fun applyTutorialInsets(view: MainPageTutorialView, promotionsTutorial: PromotionsPageTutorialView) { ViewCompat.setOnApplyWindowInsetsListener(view) { _, windowInsets -> val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) view.insetsTop = insets.top.toFloat() view.insetsLeft = insets.left.toFloat() view.insetsRight = insets.right.toFloat() view.insetsBottom = insets.bottom.toFloat() promotionsTutorial.insetsTop = insets.top.toFloat() promotionsTutorial.insetsBottom = insets.bottom.toFloat() WindowInsetsCompat.CONSUMED } } }
已排查/尝试的操作
- ✅
enableEdgeToEdge()在setContentView()之前调用 - ✅
setOnApplyWindowInsetsListener在onCreate()中setContentView()之后调用 - ❌ 在
onStart、onResume中调用enableEdgeToEdge()无效 - ❌ 配置
configChanges="uiMode"并在onConfigurationChanged()中调用enableEdgeToEdge()及手动recreate()无效 - ❌ 移除
AndroidManifest.xml中的launchMode="singleTop"无效
解决方案
方案1:手动锁定DecorView的Edge-to-Edge配置
主题切换重建后,系统可能意外重置DecorView的fitsSystemWindows属性,导致Edge-to-edge失效。在enableEdgeToEdge()后强制设置该属性为false:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d(Const.TAG_ACTIVITY, String.format("%s BaseActivity onCreate()", this.javaClass.simpleName)) enableEdgeToEdge() // 强制关闭fitsSystemWindows,确保Edge-to-edge生效 window.decorView.fitsSystemWindows = false initBinding() onActivityCreate(savedInstanceState) observeLiveData() }
方案2:主动触发Insets重新计算
Activity重建后,系统可能不会自动派发Insets事件,手动请求目标视图重新计算Insets,强制触发监听回调:
// 在你的Activity实现的onActivityCreate方法末尾添加 override fun onActivityCreate(savedInstanceState: Bundle?) { // 原有设置Insets监听的代码 InsetsManager.applyBottomNavigationViewInsets(binding.bottomContainer) InsetsManager.applyTutorialInsets(binding.tutorialView, binding.promotionsTutorial) // 强制触发Insets重新计算 ViewCompat.requestApplyInsets(binding.bottomContainer) ViewCompat.requestApplyInsets(binding.tutorialView) }
方案3:调整Insets消费逻辑
如果布局中存在多层视图需要处理Insets,不要直接返回WindowInsetsCompat.CONSUMED,改为返回原Insets,避免阻断Insets传递:
fun applyBottomNavigationViewInsets(bottomContainerView: ViewGroup) { ViewCompat.setOnApplyWindowInsetsListener(bottomContainerView) { v, windowInsets -> val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) v.setPadding(insets.left, insets.top, insets.right, insets.bottom) // 返回原Insets,允许其他视图继续处理 windowInsets } }
方案4:使用WindowInsetsController替代(可选)
通过WindowInsetsController直接控制系统栏,兼容性更稳定:
// 在BaseActivity中配置 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() window.decorView.fitsSystemWindows = false // 初始化InsetsController,控制系统栏行为 val insetsController = WindowCompat.getInsetsController(window, window.decorView) insetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE initBinding() onActivityCreate(savedInstanceState) observeLiveData() }
内容的提问来源于stack exchange,提问作者Nazar Taraniuk
相关产品推荐
相关产品推荐

