0Pricing
Android Academy · 课时

标记与摄像机

放置标记并移动摄像机

标记与摄像机 是 CoddyKit 上的免费 Android Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Android Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Android Academy 课程共包含 4 节课。

在地图上添加标记

空白地图只有地图图块。标记可以让地图承载信息——例如商店、餐厅或用户目的地的位置标记。相机则可以将视口移动到所需的位置。

在本课中,您将添加和自定义标记,将它们分组到聚类中,并在不同位置之间平滑地移动相机。

添加标记

在 maps-compose 中,标记是放置在 GoogleMap 内容 lambda 中的组合函数。每个 Marker 都需要一个保存其位置的 MarkerState。

import com.google.maps.android.compose.Marker
import com.google.maps.android.compose.rememberMarkerState

@Composable
fun MapWithMarker() {
    val position = LatLng(37.7749, -122.4194)
    GoogleMap(modifier = Modifier.fillMaxSize()) {
        Marker(
            state = rememberMarkerState(position = position),
            title = "San Francisco",
            snippet = "Welcome to SF"
        )
    }
}

标题、摘要和信息窗口

用户点击标记时,title 和 snippet 会显示在默认的信息窗口中。点击后,标题会以粗体显示,摘要显示在标题下方。

如果需要完全控制显示内容,可以提供带有自定义组合内容的 MarkerInfoWindow。

Marker(
    state = rememberMarkerState(position = LatLng(48.8584, 2.2945)),
    title = "Eiffel Tower",
    snippet = "Paris, France",
    // The info window pops up on tap by default
)

自定义标记图标

通过 icon 参数传入 BitmapDescriptor,即可更改标记的外观。defaultMarker(hue) 可以重新设置标准图钉的颜色;您也可以根据可绘制资源创建图标。

import com.google.android.gms.maps.model.BitmapDescriptorFactory

Marker(
    state = rememberMarkerState(position = LatLng(35.6895, 139.6917)),
    title = "Tokyo",
    icon = BitmapDescriptorFactory.defaultMarker(
        BitmapDescriptorFactory.HUE_AZURE
    )
)

根据数据生成多个标记

实际应用通常会根据列表渲染标记。在地图内容中遍历您的数据,并为每个项目生成一个 Marker。请为每个项目使用稳定的键。

data class Place(val name: String, val latLng: LatLng)

@Composable
fun PlacesMap(places: List<Place>) {
    GoogleMap(modifier = Modifier.fillMaxSize()) {
        places.forEach { place ->
            Marker(
                state = rememberMarkerState(position = place.latLng),
                title = place.name
            )
        }
    }
}

处理标记点击

每个 Marker 都接受一个 onClick lambda。返回 true 可消费该事件(从而阻止默认信息窗口显示),返回 false 则允许默认行为继续执行。

Marker(
    state = rememberMarkerState(position = LatLng(40.7128, -74.0060)),
    title = "New York",
    onClick = {
        // handle the tap, e.g. open a detail screen
        true // consume the event
    }
)

立即移动相机

如果要在不播放动画的情况下跳转视口,请直接设置 CameraPositionState 中的相机位置。这是一次直接切换——地图会瞬间跳到目标位置。

val cameraPositionState = rememberCameraPositionState()

// Instant move
cameraPositionState.position = CameraPosition.fromLatLngZoom(
    LatLng(51.5074, -0.1278), // London
    14f
)

为相机添加动画

为了获得更流畅的体验,请使用 animate() 为相机添加动画。它是一个挂起调用,因此请从协程作用域中启动它。使用 CameraUpdate 描述目标位置。

import com.google.android.gms.maps.CameraUpdateFactory

val scope = rememberCoroutineScope()
Button(onClick = {
    scope.launch {
        cameraPositionState.animate(
            CameraUpdateFactory.newLatLngZoom(
                LatLng(48.8566, 2.3522), 13f
            ),
            durationMs = 1000
        )
    }
}) { Text("Fly to Paris") }

让多个标记完整显示

如果要同时显示多个标记,请构建一个包含所有标记的 LatLngBounds,然后使用 newLatLngBounds 并设置像素内边距,将相机移动到该范围。

import com.google.android.gms.maps.model.LatLngBounds

val bounds = LatLngBounds.Builder().apply {
    places.forEach { include(it.latLng) }
}.build()

scope.launch {
    cameraPositionState.animate(
        CameraUpdateFactory.newLatLngBounds(bounds, 100) // 100px padding
    )
}

将多个标记聚类

数百个标记会让地图变得拥挤。maps-compose-utils 库提供了 Clustering,它会将相近的标记合并为一个气泡,并在您放大地图时将其拆分。

import com.google.maps.android.compose.clustering.Clustering

@Composable
fun ClusteredMap(items: List<MyClusterItem>) {
    GoogleMap(modifier = Modifier.fillMaxSize()) {
        Clustering(items = items)
    }
}

读取相机状态

您也可以读取相机状态。cameraPositionState.position 提供当前目标位置和缩放级别,isMoving 则会告诉您用户当前是否正在平移地图。这对于只加载可见区域的数据很有用。

LaunchedEffect(cameraPositionState.isMoving) {
    if (!cameraPositionState.isMoving) {
        val center = cameraPositionState.position.target
        // fetch data near `center`
    }
}

快速检查

您希望相机在一秒内平滑移动到新位置,而不是瞬间跳转。以下哪种方式正确?

回顾

现在,您已经可以让地图承载丰富信息并具有动态效果:

  • 将带有 title、snippet 和自定义 icon 的 Marker 组合函数放入 GoogleMap 内容中。
  • 根据数据列表渲染标记,并处理 onClick。
  • 通过 position 立即移动相机,或通过 animate() 平滑移动相机。
  • 使用 LatLngBounds 显示多个位置,并使用 Clustering 减少地图拥挤。

接下来:以正确的方式请求位置权限,让这些功能能够在真实设备上运行。

常见问题解答

「标记与摄像机」课时是免费的吗?

是的 — 「标记与摄像机」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Android Academy 课程的其余内容,请升级到 CoddyKit PRO。 Android Academy 课程共包含 4 节课。

「标记与摄像机」这节课中我会学到什么?

放置标记并移动摄像机 你通过在浏览器中直接运行的动手代码来练习 Android Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Android Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Android Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「标记与摄像机」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Android Academy 课中编写并运行代码吗?

能。每节 Android Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 显示地图
  2. 获取用户位置
  3. 标记与摄像机
  4. 正确处理位置权限
← 返回 Android Academy