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
相关产品推荐
相关产品推荐

