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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 12:47:21