Place Markers 元件
Place Markers 元件可讓您建立自訂地圖註解,直接在地圖上顯示重要地點資訊。您可以顯示地點的評分、地點類型、營業中/已關閉狀態、價格範圍或自訂文字 (例如 AI 生成的標籤或行程編號)。這樣一來,使用者就能在不需輕觸詳細地點資訊卡的情況下,獲得資訊更豐富的地圖探索體驗。
每個 PlaceMarker 可顯示一個標記圖示和選用的標記文字。
可用的圖示如下:
Type:地點類型。None:沒有圖示。
Rating:評分。PriceRange:價格範圍 (例如 $10-20)。OpenStatus:商家是否營業中。Custom:自訂文字 (例如「熱門」)。
使用地點 ID (PlaceId)、資源名稱 (ResourceName) 或地理座標 (Coordinates) 參照地點。
顯示和出處註明規定
顯示 Places UI Kit 地圖標記時,標記不得單獨顯示。點按或輕觸時,必須開啟相關聯的「地點詳細資料」元件或輕量型方塊,提供必要的 Google 品牌宣傳、貢獻者出處資訊和「回報問題」流程。
如遇空間有限的情況,請參閱相片和評論的出處資訊規定。
地點標記範例
我們提供 Jetpack Compose @Composable 函式,可消除 View 到 Compose 包裝函式的額外負擔,並避免主題不符的問題。
基本標記自訂
下列程式碼範例示範基本地點標記自訂作業,包括隱藏預設標記的三角形對話泡泡尖端。
Kotlin
import androidx.compose.runtime.Composable import com.google.android.libraries.places.compose.PlaceMarker import com.google.android.libraries.places.compose.marker.MarkerIcon import com.google.android.libraries.places.compose.marker.MarkerText import com.google.android.libraries.places.compose.PlaceReference @Composable fun DefaultMarker() { PlaceMarker( reference = PlaceReference.PlaceId("PLACE_ID"), icon = MarkerIcon.None, beakPresence = false, // remove beak ) } @Composable fun RatingMarker() { PlaceMarker( reference = PlaceReference.PlaceId("PLACE_ID"), icon = MarkerIcon.None, text = MarkerText.Rating ) } @Composable fun TypeAndRatingMarker() { PlaceMarker( reference = PlaceReference.PlaceId("PLACE_ID"), icon = MarkerIcon.Type, text = MarkerText.Rating ) } @Composable fun PriceRangeMarker() { PlaceMarker( reference = PlaceReference.ResourceName("places/PLACE_RESOURCE_NAME"), icon = MarkerIcon.None, text = MarkerText.PriceRange ) } @Composable fun OpenStatusMarker() { PlaceMarker( reference = PlaceReference.Coordinates(latitude = 37.7749, longitude = -122.4194), icon = MarkerIcon.None, text = MarkerText.OpenStatus ) } @Composable fun CustomTextMarker() { PlaceMarker( reference = PlaceReference.PlaceId("PLACE_ID"), icon = MarkerIcon.None, // Custom text accepts a String to display a number (e.g. "3") or text (e.g. "NEW") text = MarkerText.Custom("NEW") ) }
建立點擊事件監聽器
您可以提供 onClick 回呼,在輕觸獨立標記時觸發自訂動作。不過,當標記在地圖上算繪時 (例如在 MarkerComposable 內),輕觸事件會遭到地圖攔截。在這種情況下,您必須使用 MarkerComposable 的 onClick 處理常式,而不是標記的處理常式。
以下程式碼範例說明如何使用 MarkerComposable 的 onClick 處理常式。
Kotlin
import android.util.Log import androidx.compose.runtime.Composable import com.google.android.libraries.places.compose.PlaceMarker import com.google.android.libraries.places.compose.marker.MarkerIcon import com.google.android.libraries.places.compose.PlaceReference /** * PlaceMarker used outside of a map (e.g. in a list, bottom sheet, or custom overlay). * * There is no host component that provides a default click behavior here, so PlaceMarker's own * `onClick` is the only way for a developer to react to a tap. This is why `onClick` must stay * on the PlaceMarker API. */ @Composable fun ClickableMarkerWithCustomConfig() { PlaceMarker( reference = PlaceReference.PlaceId("PLACE_ID"), icon = MarkerIcon.Type, onClick = { place -> openPlaceCard(place.id) // developer might want to open the place card Log.i("PlacesMarker", "Marker was tapped by user!") } ) } /** * PlaceMarker used as the content of a MarkerComposable on a GoogleMap. * * MarkerComposable does NOT add PlaceMarker to the touch-input hierarchy: it snapshots the * composable into a bitmap and hands that bitmap to the Maps renderer as the marker icon. The * rendered marker is a map object, not a Compose node, so: * * - PlaceMarker's own `onClick` would never be invoked here (no touch events ever reach it). * - The tap is delivered by the Maps SDK to MarkerComposable's `onClick`, which is the only * click entry point on a map. * * Accordingly, the state-based `PlaceMarker(state = ...)` overload used below deliberately does * not expose an `onClick` parameter, so the correct handler is the only one available. */ @Composable fun PlaceMarkerOnMap(markerState: PlaceMarkerState) { GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = rememberCameraPositionState(), ) { markerState.location?.let { position -> MarkerComposable( state = rememberUpdatedMarkerState(position = position), // this is the click handler for the map use case. onClick = { markerState.place?.let { place -> openPlaceCard(place.id) } true // consume the event }, ) { PlaceMarker(state = markerState) } } } }
處理載入狀態和回呼
下列程式碼範例說明如何提供回呼,以處理算繪或資料遺失錯誤。
Kotlin
import android.util.Log import androidx.compose.runtime.Composable import com.google.android.libraries.places.compose.PlaceMarker import com.google.android.libraries.places.compose.marker.MarkerIcon import com.google.android.libraries.places.compose.PlaceReference @Composable fun StatusHandlingMarker() { PlaceMarker( reference = PlaceReference.PlaceId("PLACE_ID"), icon = MarkerIcon.Type, // Triggered when attribute data is successfully fetched and the marker is rendered. // Returns a minimal Place object with ID, location (LatLng), and viewport fields set. onMarkerLoaded = { place -> Log.i("PlacesMarker", "Marker successfully loaded for place ID: ${place.id}") }, // Triggered when data fetching fails or required attribute data is missing for the place. // Returns an optional Place object and an Exception describing the root cause. onMarkerLoadFailed = { place, exception -> Log.e("PlacesMarker", "Failed to load marker for place ID: ${place?.id}", exception) } ) }
使用 PlaceMarkerState 在地圖上放置標記
Place 物件會連同位置和可視區域一併傳回,協助您決定標記的放置位置。
Kotlin
// Step 1: Obtain configured handle in a single 1-line call (PlaceMarker mounted internally) val markerState = rememberPlaceMarkerState( reference = PlaceReference.PlaceId(placeId), icon = MarkerIcon.Type ) // Step 2: Pass PlaceMarkerState.location and PlaceMarker(state = PlaceMarkerState) directly to the map marker GoogleMap(...) { markerState.location?.let { position -> MarkerComposable( state = rememberMarkerState(position = position), onClick = { markerState.place?.let { place -> // Handle marker click } true } ) { PlaceMarker(state = markerState) // Render marker UI content directly on map canvas } } }
將標記縮放為自訂大小
Kotlin
// Off the map: scale with a one-line modifier. // Modifier.scale is a draw-time transform, so the bubble, beak, icon and text all scale together. @Composable fun ScaledMarker() { PlaceMarker( reference = PlaceReference.PlaceId(placeId), icon = MarkerIcon.Type, text = MarkerText.Rating, modifier = Modifier.scale(2.1f), ) } // On the map: scale by overriding LocalDensity. // MarkerComposable snapshots its content into a marker bitmap sized to the measured layout bounds. // Modifier.scale does not change those bounds, so the marker would be clipped and blurry. Scaling // the density re-measures every dp and sp inside the marker, so the bitmap, the layout bounds and // the map hit target all grow with it. @Composable fun ScaledMarkerOnMap(markerState: PlaceMarkerState) { val density = LocalDensity.current val scaledDensity = remember(density) { Density(density = density.density * 2.1f, fontScale = density.fontScale * 2.1f) } GoogleMap(...) { markerState.location?.let { position -> MarkerComposable( state = rememberUpdatedMarkerState(position = position), anchor = Offset(0.5f, 1.0f), ) { CompositionLocalProvider(LocalDensity provides scaledDensity) { PlaceMarker(state = markerState) } } } } }