Android使用Insets适配软键盘时Toolbar被裁剪问题求助
问题:API 30+ 适配软键盘时Toolbar被裁剪
之前在API 30以下,我用以下代码让界面响应屏幕键盘:
requireActivity().window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
但该方法现已废弃,于是我尝试通过Insets适配,编写了以下代码:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { requireActivity().window.setDecorFitsSystemWindows(false) requireActivity().window.decorView.setOnApplyWindowInsetsListener { v, windowInsets -> val imeHeight = windowInsets.getInsets(WindowInsets.Type.ime()) val statusBarsHeight = windowInsets.getInsets(WindowInsets.Type.statusBars()) v.setPadding( /*left */ imeHeight.left, // = 0 /*top */ statusBarsHeight.top, // = 66 /*right */ imeHeight.right, // = 0 /*bottom*/ imeHeight.bottom) // = 0 WindowInsets.Builder() .setInsets(0, imeHeight) .build() } }
现在Toolbar不再上移,但padding把Toolbar部分区域裁剪了(左侧为正常布局,右侧为异常布局):
问题原因与解决方法
核心问题
- 错误给DecorView设置padding:直接给整个DecorView设置顶部padding为状态栏高度,会让内部的Toolbar被向下挤压,同时DecorView的padding是向内的,会覆盖Toolbar顶部区域。
- Insets返回逻辑错误:自定义的Insets构建方式不符合规范,没有正确传递未处理的Insets,导致子视图无法获取正确的布局参考。
正确实现方式
方式1:直接替代旧API效果(推荐)
如果只是需要实现原SOFT_INPUT_ADJUST_RESIZE的软键盘适配效果,无需手动处理Insets,直接用新API替代:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { requireActivity().window.setSoftInputMode( WindowInsetsControllerCompat.BEHAVIOR_RESIZE_WHEN_IMMERSIVE ) } else { requireActivity().window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) }
方式2:手动处理Insets(适合自定义布局)
如果需要自定义Insets逻辑,不要直接操作DecorView的padding,而是针对具体子视图设置对应Insets:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { requireActivity().window.setDecorFitsSystemWindows(false) requireActivity().window.decorView.setOnApplyWindowInsetsListener { v, windowInsets -> val imeInsets = windowInsets.getInsets(WindowInsets.Type.ime()) val statusBarInsets = windowInsets.getInsets(WindowInsets.Type.statusBars()) // 给内部子视图分别设置Insets v.children.forEach { child -> when (child.id) { R.id.toolbar -> { // Toolbar设置顶部padding避开状态栏 child.setPadding(child.paddingLeft, statusBarInsets.top, child.paddingRight, child.paddingBottom) } R.id.content_layout -> { // 内容布局设置底部padding避开软键盘 child.setPadding(child.paddingLeft, child.paddingTop, child.paddingRight, imeInsets.bottom) } } } // 正确返回处理后的Insets,让系统继续传递 windowInsets.consume(WindowInsets.Type.ime() or WindowInsets.Type.statusBars()) } }
补充说明
setDecorFitsSystemWindows(false)会关闭系统自动Insets处理,所有布局适配需手动完成。- 避免直接修改DecorView的padding,应针对具体功能视图(如Toolbar、内容区)单独设置Insets,防止布局重叠或裁剪。
- 返回Insets时需调用
consume或传递未处理的Insets,确保子视图能获取正确的布局参考。
内容的提问来源于stack exchange,提问作者Tobias Reich
相关产品推荐
相关产品推荐

