You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Jetpack Compose的ModalBottomSheetLayout适配WebView内容尺寸

解决Jetpack Compose中ModalBottomSheetLayout自适应WebView内容高度的问题

要让ModalBottomSheetLayout根据WebView加载的内容自动调整高度,核心是让Compose感知到WebView的实际内容高度,再动态调整底部弹窗的尺寸。以下是具体实现方案:

1. 改造WebView,传递内容高度给Compose

修改WebView Composable,新增回调监听页面加载完成事件,获取内容高度并同步给Compose状态:

@SuppressLint("SetJavaScriptEnabled")
@Composable
fun WebView(url: String, onContentHeightReady: (Int) -> Unit) {
    AndroidView(factory = { context ->
        WebView(context).apply {
            // 先设置为WRAP_CONTENT,让WebView根据内容自身调整高度
            layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
            settings.builtInZoomControls = true
            settings.javaScriptEnabled = true
            settings.setSupportZoom(true)
            webChromeClient = WebChromeClient()

            webViewClient = object : WebViewClient() {
                override fun onPageFinished(view: WebView, url: String) {
                    super.onPageFinished(view, url)
                    // 将WebView内容高度转换为dp单位,同步给Compose
                    val dpHeight = (view.contentHeight * view.resources.displayMetrics.density).toInt()
                    onContentHeightReady(dpHeight)
                }

                override fun shouldOverrideUrlLoading(
                    view: WebView?,
                    request: WebResourceRequest?
                ): Boolean {
                    return true
                }

                override fun onReceivedError(
                    view: WebView?,
                    request: WebResourceRequest?,
                    error: WebResourceError?
                ) {
                    // 可根据需求添加错误处理逻辑
                }
            }
            loadUrl(url)
        }
    })
}

2. 调整ModalBottomSheetLayout配置

修改BottomSheetScreen,新增状态保存WebView内容高度,同时关闭半展开状态、限制弹窗最大高度:

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun BottomSheetScreen(url: String, content: @Composable (ModalBottomSheetState) -> Unit) {
    val bottomSheetState = rememberModalBottomSheetState(
        initialValue = ModalBottomSheetValue.Hidden,
        animationSpec = tween(
            durationMillis = 500,
            easing = FastOutLinearInEasing
        ),
        confirmValueChange = { true },
        skipHalfExpanded = true // 关闭半展开状态,直接自适应内容高度
    )
    // 保存WebView内容高度,初始值为0
    val webViewContentHeight = remember { mutableStateOf(0) }
    // 获取屏幕高度,用于限制弹窗最大高度
    val screenHeight = LocalConfiguration.current.screenHeightDp.dp

    ModalBottomSheetLayout(
        sheetState = bottomSheetState,
        sheetShape = RoundedCornerShape(18.dp),
        sheetContent = {
            // 限制弹窗最大高度为屏幕的90%,避免内容过长时占满整个屏幕
            Box(modifier = Modifier.heightIn(max = screenHeight * 0.9f)) {
                WebView(url = url) { height ->
                    webViewContentHeight.value = height
                }
            }
        },
        // 用WebView内容高度作为弹窗展开高度,初始为0
        sheetPeekHeight = webViewContentHeight.value.dp.takeIf { it > 0 } ?: 0.dp
    ) {
        content(bottomSheetState)
    }
}

关键细节说明

  • WebView布局参数:将MATCH_PARENT改为WRAP_CONTENT,让WebView先根据内容自身调整高度,再把高度同步给Compose。
  • 内容高度转换:WebView.contentHeight返回的是像素值,乘以屏幕密度转换为dp,保证在不同设备上显示一致。
  • 弹窗高度限制:通过heightIn(max = screenHeight * 0.9f)避免内容过长时弹窗占满屏幕,保留顶部可操作区域。
  • 关闭半展开:设置skipHalfExpanded = true,让弹窗直接展开到内容高度,不需要半展开的中间状态。

内容的提问来源于stack exchange,提问作者Slava

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 03:42:42