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

Android Google Maps Compose如何同时显示多个InfoWindow

解决Google Maps Compose同时显示多个InfoWindow的问题

Google Maps(包括其Compose封装库)的原生InfoWindow机制默认只支持同时显示一个窗口。你调用多个markerState.showInfoWindow()时,会触发内部状态冲突,导致系统反复切换显示状态,最终引发重绘风暴和OOM。

要实现同时显示多个信息窗口,只能通过自定义Compose组件模拟InfoWindow的方式来实现,绕过原生的限制。具体方案如下:

核心思路

放弃使用官方的MarkerInfoWindow,改用普通Marker标记位置,再在地图上层叠加自定义的可组合控件作为信息窗口,通过地图投影(Projection)将地理坐标(LatLng)转换为屏幕坐标,实现窗口与Marker的位置绑定。

代码实现示例

1. 定义自定义InfoWindow组件

@Composable
fun CustomInfoWindow(
    modifier: Modifier = Modifier,
    markerPosition: LatLng,
    mapProjection: Projection?,
    content: @Composable () -> Unit
) {
    mapProjection?.let { projection ->
        // 将地理坐标转换为屏幕坐标
        val screenCoords = projection.toScreenLocation(markerPosition)
        Box(
            modifier = modifier
                .offset(
                    // 根据Marker图标和窗口大小调整偏移,确保窗口对齐Marker顶部
                    x = (screenCoords.x - 75).dp,
                    y = (screenCoords.y - 140).dp
                )
                .background(Color.White, RoundedCornerShape(8.dp))
                .padding(horizontal = 12.dp, vertical = 8.dp)
                .shadow(elevation = 4.dp, shape = RoundedCornerShape(8.dp))
        ) {
            content()
        }
    }
}

2. 在地图中使用自定义窗口

Box(modifier = Modifier.fillMaxSize()) {
    val cameraPositionState = rememberCameraPositionState {
        position = CameraPosition.fromLatLngZoom(someLocation, 15f)
    }
    // 保存地图投影,用于坐标转换
    var mapProjection by remember { mutableStateOf<Projection?>(null) }

    GoogleMap(
        modifier = Modifier.fillMaxSize(),
        cameraPositionState = cameraPositionState,
        // 地图加载完成后获取初始投影
        onMapLoaded = { map ->
            mapProjection = map.projection
        },
        // 地图移动过程中更新投影
        cameraMoveStartedListener = {
            mapProjection = cameraPositionState.projection
        },
        // 地图停止移动后更新投影
        cameraIdleListener = {
            mapProjection = cameraPositionState.projection
        }
    ) {
        // 添加普通Marker,仅用于显示图标
        Marker(
            state = rememberMarkerState(position = someLocation),
            icon = BitMapUtils.bitmapDescriptor(context, R.drawable.ic_own_position)
        )
        Marker(
            state = rememberMarkerState(position = someLocation2),
            icon = BitMapUtils.bitmapDescriptor(context, R.drawable.ic_own_position)
        )
    }

    // 叠加两个自定义信息窗口
    CustomInfoWindow(
        markerPosition = someLocation,
        mapProjection = mapProjection
    ) {
        Text("Test 1")
    }
    CustomInfoWindow(
        markerPosition = someLocation2,
        mapProjection = mapProjection
    ) {
        Text("Test 2")
    }
}

注意事项

  • 偏移量(x/y的数值)需要根据你的Marker图标尺寸、自定义窗口的大小自行调整,确保窗口刚好显示在Marker的正上方或指定位置。
  • 监听地图的cameraMoveStarted和cameraIdle事件,保证地图移动/缩放时,窗口位置能实时更新。
  • 如果需要实现点击Marker显示/隐藏窗口的逻辑,可以给Marker添加onClick回调,维护一个布尔状态变量来控制自定义窗口的显示与否。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 04:30:58