Compile SDK 36下默认启用Edge-to-Edge时,如何恢复adjustPan原有行为以避免自动转为adjustResize
我完全懂你现在的崩溃——升级到Compile SDK 36(对应Android 16)后,系统默认开启Edge-to-Edge直接废掉了原来的adjustPan行为,全量改adjustResize适配既没人力也没预算,简直是灾难。你尝试的Compose包装思路方向是对的,但嵌套滚动组件和自动聚焦的问题确实是这类方案的常见坑,下面我给你拆解问题并给出几个优先级不同的解决办法:
一、优先尝试:系统层面强制恢复adjustPan(零布局修改)
Edge-to-Edge默认启用后,系统会偷偷修改软键盘的布局计算逻辑,但我们可以通过显式配置直接绕开这个行为,完全回到你熟悉的旧逻辑:
1. 配置Manifest+窗口标志
在你的Activity的Manifest标签里,明确指定adjustPan,同时添加半透明状态栏/导航栏的标志(这两个标志会让系统回到非Edge-to-Edge的布局计算模式):
<activity android:name=".YourTargetActivity" android:windowSoftInputMode="adjustPan" android:windowTranslucentStatus="true" android:windowTranslucentNavigation="true">
2. 代码中显式禁用Edge-to-Edge
如果你的App完全不需要Edge-to-Edge特性,在Activity的onCreate里直接关闭它,同时强制设置软键盘模式:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 关闭Edge-to-Edge,恢复系统旧的布局适配逻辑 WindowCompat.setDecorFitsSystemWindows(window, true) // 强制指定软键盘模式为adjustPan window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) }
这个方案不需要改任何布局代码,5分钟就能搞定,完全符合你“省时间省预算”的核心需求。
二、如果必须保留Edge-to-Edge:修复你的Compose包装方案
如果你因为产品需求必须保留Edge-to-Edge,那你的包装方案需要解决嵌套滚动拦截和焦点管理两个核心问题:
问题根源
你外层Compose的verticalScroll会拦截内部RecyclerView/LazyColumn的滚动事件,导致内部组件无法滚动;同时safeDrawingPadding和滚动的组合会干扰TextField的自动聚焦逻辑。
修复后的代码示例
val scrollState = rememberScrollState() val density = LocalDensity.current var measuredBaseHeightDp by remember { mutableStateOf(0.dp) } var initialSizeCaptured by remember { mutableStateOf(false) } val isImeActuallyVisible = WindowInsets.isImeVisible // 处理嵌套滚动:让内部滚动组件优先响应 val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { // 只有当内部没有可滚动内容时,才让外层处理滚动 val innerCanScroll = (binding.root.findViewByType<RecyclerView>()?.canScrollVertically(1) == true) || (binding.root.findViewByType<androidx.compose.foundation.lazy.LazyColumn>()?.canScrollVertically(1) == true) return if (!innerCanScroll && source == NestedScrollSource.Drag) { scrollState.dispatchRawDelta(available.y) available } else { Offset.Zero } } } } Box( Modifier .safeDrawingPadding() .nestedScroll(nestedScrollConnection) // 关键:启用嵌套滚动 .verticalScroll(scrollState, enabled = isImeActuallyVisible) .height(animatedBoxHeight) .onSizeChanged { size: IntSize -> if (!initialSizeCaptured && size.height > 0) { measuredBaseHeightDp = with(density) { size.height.toDp() } initialSizeCaptured = true } }, ) { AndroidView( factory = { context -> binding.root }, update = { view -> // 修复自动聚焦:IME显示时手动触发焦点 if (isImeActuallyVisible) { view.post { // 找到第一个可聚焦的EditText(可以给目标EditText加Tag精准定位) val firstFocusable = view.findViewByType<EditText>()?.takeIf { it.isFocusable } firstFocusable?.requestFocus() // 手动弹出键盘 InputMethodManager.getInstance(context) .showSoftInput(firstFocusable, InputMethodManager.SHOW_IMPLICIT) } } } ) } // 工具函数:快速查找View树中的指定类型组件 inline fun <reified T : View> View.findViewByType(): T? { if (this is T) return this for (child in (this as? ViewGroup)?.children.orEmpty()) { val result = child.findViewByType<T>() if (result != null) return result } return null }
关键修复点说明
- 嵌套滚动处理:通过
nestedScrollConnection让内部的RecyclerView/LazyColumn优先处理滚动事件,只有当内部没有可滚动内容时,外层的Compose滚动才会生效。 - 自动聚焦修复:在
AndroidView的update块中,当IME显示时手动触发目标TextField的焦点和软键盘弹出,绕开Compose和View系统的焦点冲突。 - 工具函数辅助:
findViewByType可以快速遍历View树找到内部的滚动组件或EditText,避免硬编码ID。
三、折中方案:局部禁用Edge-to-Edge
如果你的App部分页面需要Edge-to-Edge,部分页面需要旧的adjustPan,可以在需要恢复adjustPan的Activity中单独执行方案一中的代码,其他页面保留Edge-to-Edge特性,这样不需要全量修改。
总结优先级
- 方案一:零布局修改,直接回到旧行为,优先尝试,完全符合你的时间和预算要求;
- 方案三:局部适配,适合混合需求的场景;
- 方案二:适合必须保留Edge-to-Edge的场景,但需要一定的代码调整。
内容来源于stack exchange

