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

Google Maps Compose:如何将Marker固定在屏幕中央且地图可拖拽?

实现固定在屏幕中央的Marker(地图拖拽时Marker不动)

核心思路

不要将Marker嵌入地图组件内部,而是把Marker作为独立UI元素叠加在地图的屏幕中央,同时监听地图相机位置的变化,实时获取当前地图中央的经纬度。

修改后的完整代码

@OptIn(ExperimentalMaterial3Api::class, ExperimentalPermissionsApi::class)
@Composable
fun MapScreen(navController: NavHostController) {
    val (addressQuery, setAddressQuery) = remember { mutableStateOf("") }
    val initialLocation = remember { mutableStateOf(LatLng(0.132828, 100.804833)) }
    val cameraPositionState = rememberCameraPositionState {
        position = CameraPosition.fromLatLngZoom(initialLocation.value, 15f)
    }
    // 保存当前地图中央的经纬度
    val centerLatLng = remember { mutableStateOf(initialLocation.value) }
    
    val locationPermission = rememberMultiplePermissionsState(
        listOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION)
    )
    
    val uiSettings = MapUiSettings(
        compassEnabled = true,
        rotationGesturesEnabled = true,
        scrollGesturesEnabled = true,
        tiltGesturesEnabled = false,
        zoomControlsEnabled = false,
        zoomGesturesEnabled = true,
        mapToolbarEnabled = false,
        myLocationButtonEnabled = false,
        scrollGesturesEnabledDuringRotateOrZoom = true,
        indoorLevelPickerEnabled = true
    )
    
    val mapProperties = MapProperties(
        maxZoomPreference = 15f,
        minZoomPreference = 1f,
        isTrafficEnabled = false,
        mapType = MapType.NORMAL,
        isMyLocationEnabled = locationPermission.allPermissionsGranted,
    )
    
    val mapColorScheme = remember { mutableStateOf(ComposeMapColorScheme.FOLLOW_SYSTEM) }
    val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
    
    // 监听相机位置变化,实时更新中央经纬度
    LaunchedEffect(cameraPositionState.position) {
        centerLatLng.value = cameraPositionState.position.target
        // 这里可添加后续逻辑,比如根据经纬度获取地址
        println("当前中央位置:${centerLatLng.value.latitude}, ${centerLatLng.value.longitude}")
    }

    Scaffold(
        modifier = Modifier.fillMaxSize(),
        floatingActionButton = {
            FloatingActionButton(
                onClick = {
                    if (locationPermission.allPermissionsGranted) {
                        cameraPositionState.position = CameraPosition.fromLatLngZoom(initialLocation.value, 15f)
                    } else {
                        locationPermission.launchMultiplePermissionRequest()
                    }
                },
                contentColor = if (locationPermission.allPermissionsGranted) LogoBlue else Gray
            ) {
                Icon(
                    painter = painterResource(R.drawable.ic_gps),
                    contentDescription = "校准到当前位置",
                    tint = LogoBlue
                )
            }
        },
        topBar = {
            CenterAlignedTopAppBar(
                title = {
                    Image(
                        painter = painterResource(id = R.drawable.ic_pocket_small),
                        contentDescription = null
                    )
                }, 
                colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
                    containerColor = MaterialTheme.colorScheme.primaryContainer,
                    titleContentColor = MaterialTheme.colorScheme.onPrimary,
                ),
                navigationIcon = {
                    IconButton(onClick = {
                        navController.navigateUp()
                    }) {
                        Icon(
                            imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                            contentDescription = "返回箭头",
                            tint = MaterialTheme.colorScheme.onPrimary
                        )
                    }
                },
                scrollBehavior = scrollBehavior
            )
        }
    ) { paddingValues ->
        if (locationPermission.allPermissionsGranted) {
            LocationFetcher { location, string ->
                location?.let {
                    initialLocation.value = LatLng(it.latitude, it.longitude)
                    cameraPositionState.position = CameraPosition.fromLatLngZoom(LatLng(it.latitude, it.longitude), 15f)
                }
            }
        }
        Box(Modifier.padding(top = paddingValues.calculateTopPadding())) {
            GoogleMap(
                modifier = Modifier.fillMaxSize(),
                cameraPositionState = cameraPositionState,
                mapColorScheme = mapColorScheme.value,
                uiSettings = uiSettings,
                properties = mapProperties,
            ) {
                // 移除原地图内的MarkerComposable
            }
            
            // 叠加在地图中央的Marker UI
            Column(
                Modifier.fillMaxSize(),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
            ) {
                // 替换为你的自定义Marker图标或Composable
                Icon(
                    painter = painterResource(R.drawable.ic_marker),
                    contentDescription = "中央标记",
                    tint = Color.Red,
                    modifier = Modifier.size(48.dp)
                )
            }
            
            // 原有搜索视图
            Column(
                Modifier.fillMaxSize(),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                CustomSearchView(
                    addressQuery,
                    onValueChange = {
                        println(it)
                        setAddressQuery(it)
                    }) {
                    addressList()
                }
            }
        }
    }
}

关键修改点说明

  • 移除地图内的Marker:原MarkerComposable会跟随地图移动,不符合需求,直接移除。
  • 固定中央UI元素:在Box中添加居中布局的Marker图标,这个元素不受地图拖拽影响,始终停留在屏幕中央。
  • 监听相机位置:通过LaunchedEffect监听cameraPositionState.position变化,相机的target属性就是当前屏幕中央的经纬度,将其保存到centerLatLng状态中,可用于后续地址解析等操作。
  • 状态优化:用centerLatLng变量专门保存中央位置的经纬度,替代原marker变量。

此方案实现了类似Uber/Ola的选点逻辑:拖拽地图时Marker视觉上固定,同时能实时获取该位置的经纬度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 05:22:10