Android设置setDecorFitsSystemWindows为false后软键盘弹出滚动异常
调用WindowCompat.setDecorFitsSystemWindows(window, false)开启边到边显示后,系统会停止自动为应用内容适配所有系统窗口Insets,包括状态栏、导航栏、弹出的软键盘(IME)的占位适配。
没开边到边的时候,系统会自动调整窗口内容区大小,软键盘弹出时会把内容区底部顶到软键盘上方,ScrollView计算可滚动范围时拿到的是真实可见区域的高度,所以能正常滚到底。
开了边到边之后,系统就不再做这个自动调整了,页面内容默认铺满整个屏幕,直接铺到软键盘后面。这时候ScrollView算可滚动高度的时候,还把屏幕物理底部当自己的边界,根本不知道软键盘挡了底部一块区域,滚到它计算出来的最底位置时,最后几个条目刚好落在软键盘覆盖的区域里,自然就看不到了。你现在只能滚到内容为4的输入框,就是因为软键盘的遮挡高度刚好盖住了5、6两个输入框的位置。
这是边到边适配的标准处理场景,只要手动给可滚动容器处理IME类型的WindowInsets就行,两种常用实现:
方案1:快速适配(简单页面用)
直接在ScrollView的布局节点上加android:fitsSystemWindows="true"属性:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical"> <!-- 内部子布局不用改 --> </ScrollView>
加了这个属性后,View会自动消费系统下发的所有窗口Insets,自动给自身加上对应状态栏、导航栏、软键盘的padding,ScrollView计算滚动范围的时候就会把被遮挡的区域刨掉,滚动就正常了。
方案2:手动控制Insets(推荐,灵活度高)
如果页面有复杂沉浸式效果、需要自定义边距规则,就手动给ScrollView设置Insets监听,自己控制padding:
- 先确保项目依赖了AndroidX Core库(新建Android项目默认都会带):
implementation "androidx.core:core-ktx:1.12.0" // 用当前最新版本即可 - 在Activity的
onCreate里,setContentView之后加如下代码:ViewCompat.setOnApplyWindowInsetsListener(binding.scrollView) { scrollView, insets -> // 分别拿软键盘、系统导航栏的底部占位高度 val imeBottom = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom val navBarBottom = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom // 取两者最大值当底部padding:软键盘弹起时用软键盘高度,收起时用导航栏高度 scrollView.updatePadding(bottom = maxOf(imeBottom, navBarBottom)) // 把insets继续传给子View,避免子View拿不到insets出问题 insets }
这个方案兼容性更好,能覆盖Android 11以下的低版本,也可以根据业务需求调整padding规则,不会和其他沉浸式效果冲突。
注意:开了边到边之后,别指望Manifest里配的
android:windowSoftInputMode="adjustResize"能自动生效,这个配置在关闭setDecorFitsSystemWindows之后不会再自动调整布局高度,所有系统栏、软键盘的适配都要通过WindowInsets相关API手动处理。
内容的提问来源于stack exchange,提问作者Cameron Henige

