Jetpack Compose中如何让Marker随当前位置移动并旋转?
解决Jetpack Compose中Google Maps Marker不随位置移动的问题
问题根源
你的Marker保持静止是因为rememberMarkerState仅在初始组合时创建并设置位置,当viewModel.origin更新时,Marker状态没有随之同步更新。此外,旋转角度也没有根据位置变化重新计算。
解决方案
1. 让Marker随位置移动
通过LaunchedEffect监听origin的变化,每次位置更新时主动同步Marker状态的位置:
@Composable fun GoogleMarkers(origin: LatLng, destination: LatLng) { // 初始化Marker状态,绑定初始位置 val originMarkerState = rememberMarkerState(position = origin) // 监听origin变化,实时更新Marker位置 LaunchedEffect(origin) { originMarkerState.position = origin } // ... 其余Marker代码 }
2. 动态更新Marker旋转角度
根据需求,有两种常见的旋转实现方式:
方式A:始终朝向目的地
每次origin或destination变化时,重新计算两者之间的方位角:
@Composable fun GoogleMarkers(origin: LatLng, destination: LatLng) { val originMarkerState = rememberMarkerState(position = origin) LaunchedEffect(origin) { originMarkerState.position = origin } // 当origin或destination变化时重新计算方位角 val bearingToDestination = remember(origin, destination) { bearingBetweenLocations(origin, destination).toFloat() } Marker( state = originMarkerState, title = "Origin", snippet = "Origin of the location", icon = bitmapDescriptorFromVector(LocalContext.current, R.drawable.car, 160, 160), rotation = bearingToDestination ) // ... 目的地Marker代码 }
方式B:根据移动方向旋转
如果需要Marker随移动方向旋转,需在ViewModel中记录上一次的位置,计算当前位置与历史位置的方位角:
ViewModel中添加位置跟踪逻辑:
class MapViewModel : ViewModel() { var origin by mutableStateOf<LatLng?>(null) private var previousOrigin: LatLng? = null var movementBearing by mutableStateOf(0f) fun updateLocation(newLocation: LatLng) { previousOrigin = origin origin = newLocation previousOrigin?.let { prev -> movementBearing = bearingBetweenLocations(prev, newLocation).toFloat() } } }
在GoogleMarkers中使用移动方位角:
@Composable fun GoogleMarkers(origin: LatLng, movementBearing: Float, destination: LatLng) { val originMarkerState = rememberMarkerState(position = origin) LaunchedEffect(origin) { originMarkerState.position = origin } Marker( state = originMarkerState, title = "Origin", snippet = "Origin of the location", icon = bitmapDescriptorFromVector(LocalContext.current, R.drawable.car, 160, 160), rotation = movementBearing ) // ... 目的地Marker代码 }
完整修正后的代码
以下是整合了移动和朝向目的地旋转的完整实现:
@Composable fun GoogleMapScreen(viewModel: MapViewModel) { val cameraPositionState = rememberCameraPositionState() val destination = LatLng(28.5535, 77.2588) // 示例目的地坐标 val directionsResult by viewModel.directionsResult.collectAsState() GoogleMap( cameraPositionState = cameraPositionState, modifier = Modifier.weight(1f), properties = MapProperties(isMyLocationEnabled = true), uiSettings = MapUiSettings( compassEnabled = true, indoorLevelPickerEnabled = false, myLocationButtonEnabled = true ) ) { viewModel.origin?.let { userLocation -> GoogleMarkers(origin = userLocation, destination = destination) } directionsResult?.routes?.forEach { route -> Polyline( points = route.overviewPolyline.decodePath().map { LatLng(it.lat, it.lng) }, color = Color(0xFF2196F3), jointType = JointType.DEFAULT, width = 12f ) } } } @Composable fun GoogleMarkers(origin: LatLng, destination: LatLng) { val originMarkerState = rememberMarkerState(position = origin) LaunchedEffect(origin) { originMarkerState.position = origin } val bearingToDestination = remember(origin, destination) { bearingBetweenLocations(origin, destination).toFloat() } Marker( state = originMarkerState, title = "Origin", snippet = "Origin of the location", icon = bitmapDescriptorFromVector(LocalContext.current, R.drawable.car, 160, 160), rotation = bearingToDestination ) Marker( state = rememberMarkerState(position = destination), title = "Lotus Temple", snippet = "The Lotus temple", icon = bitmapDescriptorFromVector( LocalContext.current, R.drawable.destination, 150, 150 ) ) } // 工具函数:计算两个坐标之间的方位角 fun bearingBetweenLocations(start: LatLng, end: LatLng): Double { val lat1 = Math.toRadians(start.latitude) val lon1 = Math.toRadians(start.longitude) val lat2 = Math.toRadians(end.latitude) val lon2 = Math.toRadians(end.longitude) val dLon = lon2 - lon1 val y = Math.sin(dLon) * Math.cos(lat2) val x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon) val bearing = Math.atan2(y, x) return Math.toDegrees(bearing) }
关键要点
- 使用
LaunchedEffect监听位置变化,主动更新Marker状态的position属性 - 利用
remember(key1, key2)在依赖项变化时重新计算旋转角度 - 如果需要基于移动方向旋转,需在ViewModel中跟踪历史位置并计算移动方位角
内容的提问来源于stack exchange,提问作者Saurabh Jain
相关产品推荐
相关产品推荐

