背景是GP要求再2025.8.31之前,在GP上过审的应用都要升级targetversion到35。
我维护的app目前target version为34,所以需要升级到35。
升级适配中遇到的问题主要是这个:
https://developer.android.com/develop/ui/views/layout/edge-to-edge?hl=zh-cn
即修改targetverison=35,系统会默认以全面屏的方式展示app,这会导致statusbar和navigationbar的位置会被应用内容默认撑满。
适配方法:
方案一:
- 设置padding,强制保留statusbar、NavigationBar的区域为空,用空内容把这些区域撑开。
方案二:
- 使用DecorView的默认处理。即在DecorView中,当窗口插入到达时,如果应用没有明确要求边到边显示(即没有调用 setDecorFitsSystemWindows(window, false)),那么 DecorView 会消费系统窗口插入,并为内容视图设置内边距。
- 这种场景 ViewCompat.setOnApplyWindowInsetsListener 不会收到回调,所以 BottomNavigationView 也不会收到回调,所以旧版本是可以兼容的。
- 去掉了 Activity 中对子view设置的 fitsystemwindow 的属性
- 关闭了 BottomNavigationView 对全面屏的自动适配
在 Android 中,当 targetSdkVersion = 34
(Android 14)且未正确配置全面屏(Edge-to-Edge)属性时,ViewCompat.setOnApplyWindowInsetsListener
可能无法收到回调。这是因为系统默认的 DecorView 或 系统控件 消费了 WindowInsets 事件。以下是关键原因和解决方案:
1. 根本原因:DecorView 消费了 WindowInsets
- Android 系统的窗口插入(WindowInsets)分发从
DecorView
开始。如果未启用全面屏模式(即未设置WindowCompat.setDecorFitsSystemWindows(window, false)
),系统默认会为 DecorView 自动添加内边距(如状态栏/导航栏占位),导致事件被消费。 - 关键类:
com.android.internal.policy.DecorView
(系统内部类)会处理 WindowInsets,并在未启用全面屏时直接消费事件,阻止其传递给子 View。
2. 系统默认行为的影响
- 当
targetSdkVersion >= 30
时,Android 默认启用手势导航,但应用仍需主动声明全面屏才能正确处理 WindowInsets。 - 未设置全面屏时,系统会通过以下方式消费事件:
- DecorView 自动应用系统栏的内边距。
- 系统控件(如
CoordinatorLayout
、DrawerLayout
)若在布局中,可能优先消费事件。
3. 解决方案:启用全面屏模式
在 Activity 的 onCreate
中调用以下代码:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 关键:允许内容延伸到系统栏下方
WindowCompat.setDecorFitsSystemWindows(window, false)
setContentView(R.layout.your_layout)
val rootView = findViewById<View>(R.id.root)
ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, insets ->
// 现在可以收到回调
insets
}
}
4. 额外配置
- 主题设置:在
res/values-v34/themes.xml
中禁用强制非全面屏:<style name="AppTheme" parent="Theme.Material3.DayNight"> <item name="android:windowFullscreen">false</item> <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> </style>
- 处理视觉冲突:使用
WindowInsetsCompat
调整 UI 避免遮挡:ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) view.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) insets }
5. 验证是否生效
- 检查是否调用
WindowCompat.setDecorFitsSystemWindows(window, false)
。 - 确保监听器设置在根布局(如
ConstraintLayout
)而非子 View 上。 - 避免使用
fitsSystemWindows="true"
(除非明确需要系统自动处理)。
小结
当未启用全面屏时,DecorView 会消费 WindowInsets 事件,导致监听器失效。通过主动调用 WindowCompat.setDecorFitsSystemWindows(window, false)
并配置主题,系统会将事件传递给应用层级的 View,从而触发 OnApplyWindowInsetsListener
回调。这是 Android 14 全面屏行为的预期设计。