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

Compose中AndroidView嵌套WebView禁用用户交互的实现方案

在Compose的BottomSheet中禁用WebView用户交互的解决方案

问题背景

在Compose的非模态BottomSheet中,通过AndroidView嵌入了加载谷歌地图站点的WebView,用于展示通过FusedLocationProviderClient获取的位置信息。需要禁用WebView的用户交互,已知原生Android的方案是添加遮罩或重写触摸事件,但不清楚在Compose的AndroidView中如何实现。

现有BottomSheet代码

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LocationScreen(startLocationUpdates: ()->Unit, context: Context, currentLocation: LatLng) {

val permissions = arrayOf(
    Manifest.permission.ACCESS_COARSE_LOCATION,
    Manifest.permission.ACCESS_FINE_LOCATION,
)

val launchMultiplePermissions = rememberLauncherForActivityResult(
    ActivityResultContracts.RequestMultiplePermissions()
) { permissionMaps ->
    val areGranted = permissionMaps.values.reduce { acc, next -> acc && next }
    if (areGranted) {
        locationRequired = true
        startLocationUpdates()
        Toast.makeText(context, "Permission Granted", Toast.LENGTH_SHORT).show()
    } else {
        Toast.makeText(context, "Permission Denied", Toast.LENGTH_SHORT).show()
    }
}

var readable by remember { mutableStateOf("") }
//val scope = rememberCoroutineScope() 
//I HAVE THIS COMMENTED BECAUSE THE BUTTON THAT IS USUALLY LOCATED IN THE BOTTOMSHEET TO CLOSE IT IS COVER BY THE WEBVIEW AND CAN'T BE USED
val scaffoldState = rememberBottomSheetScaffoldState()

BottomSheetScaffold(
    scaffoldState = scaffoldState,
    sheetPeekHeight = 590.dp, //The Bottom Sheet will show the WebView partially but all that is needed
    sheetSwipeEnabled = false, //For the  moment I don't want the Bottom Sheet to occupy the full screen
    sheetContent = {
        
        Column(
            Modifier
                .fillMaxWidth()
                .padding(6.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            if (currentLocation.latitude != 0.toDouble() && currentLocation.longitude != 0.toDouble() && readable != "") {
                WebViewScreen(location = currentLocation) //HERE IS THE CALL TO THE ANDROIDVIEW/WEBVIEW
            } else {Text(text = "Please click [Get your location]")}
        }
    }) { innerPadding ->
    Box(Modifier.padding(innerPadding)) {
        Column(
            modifier = Modifier.fillMaxWidth(),
            verticalArrangement = Arrangement.SpaceEvenly,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(text = "Your location: ${currentLocation.latitude}/${currentLocation.longitude}")
            if (currentLocation.latitude != 0.toDouble() && currentLocation.longitude != 0.toDouble()) {
                Text(
                    text = "Readable location: $currentLocation"
                )
                location = ""
                readable = getReadableLocation(currentLocation.latitude, currentLocation.longitude, context) //HERE I USE A GEOCODER TO GET THE ADDRESS
                glocation = currentLocation
            }
            Button(onClick = {
                if (permissions.all {
                        ContextCompat.checkSelfPermission(
                            context,
                            it
                        ) == PackageManager.PERMISSION_GRANTED
                    }) {
                    //Get Locations
                    startLocationUpdates() //THIS IS WHERE I GET THE LOCATION/COORDINATES
                } else {
                    launchMultiplePermissions.launch(permissions)
                }
                //run = true
            }) {
                Text(text = "Get your location")
            }
        }
    }
}

现有WebView代码

@Composable
fun WebViewScreen(location: LatLng){ 

//val urladdress = URLDecoder.decode(address,"utf-8")
val url = "http://maps.google.com/maps?z=16&t=h&q=loc:${location.latitude}+${location.longitude}&hl=${Locale.getDefault().language}"

AndroidView(
    factory = { context ->
        WebView(context).apply {
            layoutParams = ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )
            settings.javaScriptEnabled = true
            webViewClient = WebViewClient()

            settings.loadWithOverviewMode = true
            settings.useWideViewPort = true
            settings.setSupportZoom(true)
            loadUrl(url)
        }
    },
    update = { webView ->
        webView.loadUrl(url)
    }
)

解决方案

方案1:直接在WebView配置中禁用交互

这是最直接的方式,通过WebView的内置属性关闭所有用户交互,不需要额外的Compose层处理。修改WebViewScreen中的WebView初始化代码:

@Composable
fun WebViewScreen(location: LatLng){ 
    val url = "http://maps.google.com/maps?z=16&t=h&q=loc:${location.latitude}+${location.longitude}&hl=${Locale.getDefault().language}"

    AndroidView(
        factory = { context ->
            WebView(context).apply {
                layoutParams = ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT
                )
                settings.javaScriptEnabled = true
                webViewClient = WebViewClient()

                settings.loadWithOverviewMode = true
                settings.useWideViewPort = true
                // 禁用缩放相关设置
                settings.setSupportZoom(false)
                settings.builtInZoomControls = false
                settings.displayZoomControls = false
                // 核心:禁用WebView的所有用户输入
                isEnabled = false
                // 可选:完全屏蔽触摸事件
                setOnTouchListener { _, _ -> true }
                loadUrl(url)
            }
        },
        update = { webView ->
            webView.loadUrl(url)
        }
    )
}

关键修改点:

  • isEnabled = false:直接禁用WebView的所有用户输入
  • 关闭缩放相关设置,避免用户触发缩放操作
  • 可选添加setOnTouchListener,拦截所有触摸事件确保完全无交互

方案2:在Compose层添加遮罩层

如果需要保留WebView的内部状态但屏蔽外部触摸,可以在Compose中给WebView添加一个透明遮罩,拦截所有触摸事件:

@Composable
fun WebViewScreen(location: LatLng){ 
    val url = "http://maps.google.com/maps?z=16&t=h&q=loc:${location.latitude}+${location.longitude}&hl=${Locale.getDefault().language}"

    Box(Modifier.fillMaxSize()) {
        AndroidView(
            factory = { context ->
                WebView(context).apply {
                    layoutParams = ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT
                    )
                    settings.javaScriptEnabled = true
                    webViewClient = WebViewClient()

                    settings.loadWithOverviewMode = true
                    settings.useWideViewPort = true
                    settings.setSupportZoom(true)
                    loadUrl(url)
                }
            },
            update = { webView ->
                webView.loadUrl(url)
            }
        )
        // 透明遮罩,拦截所有触摸事件
        Box(
            modifier = Modifier
                .fillMaxSize()
                .pointerInput(Unit) {
                    awaitPointerEventScope {
                        while (true) {
                            awaitPointerEvent()
                            // 不处理任何触摸事件,直接拦截
                        }
                    }
                }
        )
    }
}

这种方式不需要修改WebView内部配置,适合需要WebView保持活跃但不允许用户操作的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 22:04:55