mapbox-android-patterns by mapbox/mapbox-agent-skills
npx skills add https://github.com/mapbox/mapbox-agent-skills --skill mapbox-android-patterns在 Android 上使用 Kotlin、Jetpack Compose 和 View 系统集成 Mapbox Maps SDK v11 的官方模式。
在以下情况下使用此技能:
官方资源:
创建 app/res/values/mapbox_access_token.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="mapbox_access_token" translatable="false"
tools:ignore="UnusedResources">YOUR_MAPBOX_ACCESS_TOKEN</string>
</resources>
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
获取您的令牌: 登录 mapbox.com
在 settings.gradle.kts 中:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://api.mapbox.com/downloads/v2/releases/maven")
}
}
}
在模块 build.gradle.kts 中:
android {
defaultConfig {
minSdk = 21
}
}
dependencies {
implementation("com.mapbox.maps:android:11.18.1")
}
对于 Jetpack Compose:
dependencies {
implementation("com.mapbox.maps:android:11.18.1")
implementation("com.mapbox.extension:maps-compose:11.18.1")
}
基础地图:
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import com.mapbox.maps.extension.compose.*
import com.mapbox.maps.Style
import com.mapbox.geojson.Point
@Composable
fun MapScreen() {
MapboxMap(
modifier = Modifier.fillMaxSize()
) {
// 通过 MapEffect 初始化摄像头(默认加载 Style.STANDARD)
MapEffect(Unit) { mapView ->
// 设置初始摄像头位置
mapView.mapboxMap.setCamera(
CameraOptions.Builder()
.center(Point.fromLngLat(-122.4194, 37.7749))
.zoom(12.0)
.build()
)
}
}
}
带装饰控件:
MapboxMap(
modifier = Modifier.fillMaxSize(),
scaleBar = {
ScaleBar(
enabled = true,
position = Alignment.BottomStart
)
},
compass = {
Compass(enabled = true)
}
) {
// 默认加载 Style.STANDARD
}
布局 XML (activity_map.xml):
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mapbox.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Activity:
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.mapbox.maps.MapView
import com.mapbox.maps.Style
import com.mapbox.geojson.Point
class MapActivity : AppCompatActivity() {
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
mapView = findViewById(R.id.mapView)
mapView.mapboxMap.setCamera(
CameraOptions.Builder()
.center(Point.fromLngLat(-122.4194, 37.7749))
.zoom(12.0)
.build()
)
mapView.mapboxMap.loadStyle(Style.STANDARD)
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}
点标注是地图上标记位置最常见的方式。
Jetpack Compose:
MapboxMap(modifier = Modifier.fillMaxSize()) {
MapEffect(Unit) { mapView ->
// 首先加载样式
mapView.mapboxMap.loadStyle(Style.STANDARD)
// 创建标注管理器并添加标记
val annotationManager = mapView.annotations.createPointAnnotationManager()
val pointAnnotation = PointAnnotationOptions()
.withPoint(Point.fromLngLat(-122.4194, 37.7749))
.withIconImage("custom-marker")
annotationManager.create(pointAnnotation)
}
}
// 注意:Compose 没有声明式的 PointAnnotation 组件
// 标记必须通过 MapEffect 命令式地添加
View 系统:
// 创建标注管理器(创建一次,重复使用以进行更新)
val pointAnnotationManager = mapView.annotations.createPointAnnotationManager()
// 创建标记
val pointAnnotation = PointAnnotationOptions()
.withPoint(Point.fromLngLat(-122.4194, 37.7749))
.withIconImage("custom-marker")
pointAnnotationManager.create(pointAnnotation)
多个标记:
val locations = listOf(
Point.fromLngLat(-122.4194, 37.7749),
Point.fromLngLat(-122.4094, 37.7849),
Point.fromLngLat(-122.4294, 37.7649)
)
val annotations = locations.map { point ->
PointAnnotationOptions()
.withPoint(point)
.withIconImage("marker")
}
pointAnnotationManager.create(annotations)
val circleAnnotationManager = mapView.annotations.createCircleAnnotationManager()
val circle = CircleAnnotationOptions()
.withPoint(Point.fromLngLat(-122.4194, 37.7749))
.withCircleRadius(10.0)
.withCircleColor("#FF0000")
circleAnnotationManager.create(circle)
val polylineAnnotationManager = mapView.annotations.createPolylineAnnotationManager()
val polyline = PolylineAnnotationOptions()
.withPoints(listOf(point1, point2, point3))
.withLineColor("#0000FF")
.withLineWidth(4.0)
polylineAnnotationManager.create(polyline)
val polygonAnnotationManager = mapView.annotations.createPolygonAnnotationManager()
val points = listOf(listOf(coord1, coord2, coord3, coord1)) // 闭合多边形
val polygon = PolygonAnnotationOptions()
.withPoints(points)
.withFillColor("#0000FF")
.withFillOpacity(0.5)
polygonAnnotationManager.create(polygon)
步骤 1:在 AndroidManifest.xml 中添加权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
步骤 2:请求权限并显示位置:
// 首先请求权限(使用 ActivityResultContracts)
// 显示位置指示器
mapView.location.updateSettings {
enabled = true
puckBearingEnabled = true
}
使摄像头在用户移动时跟随用户位置:
class MapActivity : AppCompatActivity() {
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
mapView = findViewById(R.id.mapView)
mapView.mapboxMap.loadStyle(Style.STANDARD)
setupLocationTracking()
}
private fun setupLocationTracking() {
// 首先请求权限(使用 ActivityResultContracts)
// 显示用户位置
mapView.location.updateSettings {
enabled = true
puckBearingEnabled = true
}
// 摄像头跟随用户位置
mapView.location.addOnIndicatorPositionChangedListener { point ->
mapView.camera.easeTo(
CameraOptions.Builder()
.center(point)
.zoom(15.0)
.pitch(45.0)
.build(),
MapAnimationOptions.Builder()
.duration(1000)
.build()
)
}
// 可选:同时跟随方位(方向)
mapView.location.addOnIndicatorBearingChangedListener { bearing ->
mapView.camera.easeTo(
CameraOptions.Builder()
.bearing(bearing)
.build(),
MapAnimationOptions.Builder()
.duration(1000)
.build()
)
}
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}
mapView.location.getLastLocation { location ->
location?.let {
val point = Point.fromLngLat(it.longitude, it.latitude)
mapView.camera.easeTo(
CameraOptions.Builder()
.center(point)
.zoom(14.0)
.build()
)
}
}
使用 GeoJSON 源和图层将您自己的数据添加到地图。
// 为线创建坐标
val routeCoordinates = listOf(
Point.fromLngLat(-122.4194, 37.7749),
Point.fromLngLat(-122.4094, 37.7849),
Point.fromLngLat(-122.3994, 37.7949)
)
// 创建 GeoJSON 源
val geoJsonSource = geoJsonSource("route-source") {
geometry(LineString.fromLngLats(routeCoordinates))
}
mapView.mapboxMap.style?.addSource(geoJsonSource)
// 创建线图层
val lineLayer = lineLayer("route-layer", "route-source") {
lineColor(Color.BLUE)
lineWidth(4.0)
lineCap(LineCap.ROUND)
lineJoin(LineJoin.ROUND)
}
mapView.mapboxMap.style?.addLayer(lineLayer)
val polygonCoordinates = listOf(
listOf(coord1, coord2, coord3, coord1) // 闭合多边形
)
val geoJsonSource = geoJsonSource("area-source") {
geometry(Polygon.fromLngLats(polygonCoordinates))
}
mapView.mapboxMap.style?.addSource(geoJsonSource)
val fillLayer = fillLayer("area-fill", "area-source") {
fillColor(Color.parseColor("#0000FF"))
fillOpacity(0.3)
fillOutlineColor(Color.parseColor("#0000FF"))
}
mapView.mapboxMap.style?.addLayer(fillLayer)
val geojsonString = """
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [-122.4194, 37.7749]},
"properties": {"name": "Location 1"}
},
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [-122.4094, 37.7849]},
"properties": {"name": "Location 2"}
}
]
}
"""
val geoJsonSource = geoJsonSource("points-source") {
data(geojsonString)
}
mapView.mapboxMap.style?.addSource(geoJsonSource)
val symbolLayer = symbolLayer("points-layer", "points-source") {
iconImage("marker")
textField(Expression.get("name"))
textOffset(listOf(0.0, 1.5))
}
mapView.mapboxMap.style?.addLayer(symbolLayer)
mapView.mapboxMap.style?.getLayerAs<LineLayer>("route-layer")?.let { layer ->
layer.lineColor(Color.RED)
layer.lineWidth(6.0)
}
mapView.mapboxMap.style?.removeStyleLayer("route-layer")
mapView.mapboxMap.style?.removeStyleSource("route-source")
// Compose - 更新摄像头状态
cameraState.position = CameraPosition(
center = Point.fromLngLat(-74.0060, 40.7128),
zoom = 14.0,
bearing = 90.0,
pitch = 60.0
)
// Views - 立即执行
mapView.mapboxMap.setCamera(
CameraOptions.Builder()
.center(Point.fromLngLat(-74.0060, 40.7128))
.zoom(14.0)
.bearing(90.0)
.pitch(60.0)
.build()
)
// 飞行动画(弧形轨迹)
mapView.camera.flyTo(
CameraOptions.Builder()
.center(destination)
.zoom(15.0)
.build(),
MapAnimationOptions.Builder()
.duration(2000)
.build()
)
// 缓动动画(平滑)
mapView.camera.easeTo(
CameraOptions.Builder()
.center(destination)
.zoom(15.0)
.build(),
MapAnimationOptions.Builder()
.duration(1000)
.build()
)
val coordinates = listOf(coord1, coord2, coord3)
val camera = mapView.mapboxMap.cameraForCoordinates(
coordinates,
EdgeInsets(50.0, 50.0, 50.0, 50.0),
bearing = 0.0,
pitch = 0.0
)
mapView.camera.easeTo(camera)
// Compose - 通过 MapEffect 加载样式
MapboxMap(modifier = Modifier.fillMaxSize()) {
MapEffect(Unit) { mapView ->
// 默认加载 Style.STANDARD,仅在其他样式时需要显式加载
// mapView.mapboxMap.loadStyle(Style.STREETS) // Mapbox Streets
// mapView.mapboxMap.loadStyle(Style.OUTDOORS) // Mapbox Outdoors
// mapView.mapboxMap.loadStyle(Style.LIGHT) // Mapbox Light
// mapView.mapboxMap.loadStyle(Style.DARK) // Mapbox Dark
// mapView.mapboxMap.loadStyle(Style.STANDARD_SATELLITE) // 卫星图像
// mapView.mapboxMap.loadStyle(Style.SATELLITE_STREETS) // 卫星 + 街道
}
}
// Views
mapView.mapboxMap.loadStyle(Style.STANDARD)
mapView.mapboxMap.loadStyle(Style.DARK)
val customStyleUrl = "mapbox://styles/username/style-id"
// Compose
MapboxMap(modifier = Modifier.fillMaxSize()) {
MapEffect(Unit) { mapView ->
mapView.mapboxMap.loadStyle(customStyleUrl)
}
}
// Views
mapView.mapboxMap.loadStyle(customStyleUrl)
现代的 Interactions API 允许处理地图要素上的点击事件,并提供类型化的要素访问。适用于 Standard 样式的预定义要素集,如 POI、建筑物和地点标签。
View 系统模式:
import com.mapbox.maps.interactions.ClickInteraction
class MapActivity : AppCompatActivity() {
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
mapView = findViewById(R.id.mapView)
mapView.mapboxMap.loadStyle(Style.STANDARD)
setupFeatureInteractions()
}
private fun setupFeatureInteractions() {
// 点击 POI 要素
mapView.mapboxMap.addInteraction(
ClickInteraction.standardPoi { poi, context ->
Log.d("MapTap", "Tapped POI: ${poi.name}")
true // 停止传播
}
)
// 点击建筑物
mapView.mapboxMap.addInteraction(
ClickInteraction.standardBuildings { building, context ->
Log.d("MapTap", "Tapped building")
// 高亮显示建筑物
mapView.mapboxMap.setFeatureState(
building,
StandardBuildingsState {
highlight(true)
}
)
true
}
)
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}
Jetpack Compose 模式:
@Composable
fun MapScreen() {
MapboxMap(modifier = Modifier.fillMaxSize()) {
MapEffect(Unit) { mapView ->
// 加载 Standard 样式
mapView.mapboxMap.loadStyle(Style.STANDARD)
// 使用 View 系统 API 添加要素集交互
mapView.mapboxMap.addInteraction(
ClickInteraction.standardPoi { poi, context ->
Log.d("MapTap", "Tapped POI: ${poi.name}")
true
}
)
mapView.mapboxMap.addInteraction(
ClickInteraction.standardBuildings { building, context ->
Log.d("MapTap", "Tapped building")
mapView.mapboxMap.setFeatureState(
building,
state = mapOf("select" to true)
)
true
}
)
}
}
}
// 注意:Compose 中的要素集交互使用 MapEffect 来访问
// 底层的 MapView 并使用 View 系统的交互 API
### 点击自定义图层
```kotlin
mapView.mapboxMap.addInteraction(
ClickInteraction.layer("custom-layer-id") { feature, context ->
Log.d("MapTap", "Feature properties: ${feature.properties()}")
true
}
)
import com.mapbox.maps.interactions.LongClickInteraction
mapView.mapboxMap.addInteraction(
LongClickInteraction.standardPoi { poi, context ->
Log.d("MapTap", "Long pressed POI: ${poi.name}")
true
}
)
mapView.gestures.addOnMapClickListener { point ->
Log.d("MapClick", "Tapped at: ${point.latitude()}, ${point.longitude()}")
true // 消费事件
}
// 禁用特定手势
mapView.gestures.pitchEnabled = false
mapView.gestures.rotateEnabled = false
// 配置缩放限制
mapView.mapboxMap.setCamera(
CameraOptions.Builder()
.zoom(12.0)
.build()
)
// ❌ 不要重复创建新的管理器
fun updateMarkers() {
val manager = mapView.annotations.createPointAnnotationManager()
manager.create(markers)
}
// ✅ 创建一次,重复使用
val pointAnnotationManager = mapView.annotations.createPointAnnotationManager()
fun updateMarkers() {
pointAnnotationManager.deleteAll()
pointAnnotationManager.create(markers)
}
// ✅ 一次性全部创建
pointAnnotationManager.create(allAnnotations)
// ❌ 不要逐个创建
allAnnotations.forEach { annotation ->
pointAnnotationManager.create(annotation)
}
// 始终调用生命周期方法
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
// ✅ Standard 样式经过优化并推荐使用
Style.STANDARD
// 仅在特定用例需要时使用其他样式
Style.STANDARD_SATELLITE // 卫星图像
检查:
mapbox_access_token.xml 中的令牌mapView.mapboxMap.subscribeStyleLoaded { _ ->
Log.d("Map", "Style loaded successfully")
// 在此处添加图层和源
}
Style.STANDARD(推荐且经过优化)每周安装次数
174
代码仓库
GitHub 星标数
35
首次出现
2026年2月1日
安全审计
安装于
gemini-cli154
opencode154
codex151
github-copilot149
cursor143
kimi-cli140
Official patterns for integrating Mapbox Maps SDK v11 on Android with Kotlin, Jetpack Compose, and View system.
Use this skill when:
Official Resources:
Create app/res/values/mapbox_access_token.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="mapbox_access_token" translatable="false"
tools:ignore="UnusedResources">YOUR_MAPBOX_ACCESS_TOKEN</string>
</resources>
Get your token: Sign in at mapbox.com
In settings.gradle.kts:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://api.mapbox.com/downloads/v2/releases/maven")
}
}
}
In module build.gradle.kts:
android {
defaultConfig {
minSdk = 21
}
}
dependencies {
implementation("com.mapbox.maps:android:11.18.1")
}
For Jetpack Compose:
dependencies {
implementation("com.mapbox.maps:android:11.18.1")
implementation("com.mapbox.extension:maps-compose:11.18.1")
}
Basic map:
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import com.mapbox.maps.extension.compose.*
import com.mapbox.maps.Style
import com.mapbox.geojson.Point
@Composable
fun MapScreen() {
MapboxMap(
modifier = Modifier.fillMaxSize()
) {
// Initialize camera via MapEffect (Style.STANDARD loads by default)
MapEffect(Unit) { mapView ->
// Set initial camera position
mapView.mapboxMap.setCamera(
CameraOptions.Builder()
.center(Point.fromLngLat(-122.4194, 37.7749))
.zoom(12.0)
.build()
)
}
}
}
With ornaments:
MapboxMap(
modifier = Modifier.fillMaxSize(),
scaleBar = {
ScaleBar(
enabled = true,
position = Alignment.BottomStart
)
},
compass = {
Compass(enabled = true)
}
) {
// Style.STANDARD loads by default
}
Layout XML (activity_map.xml):
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mapbox.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Activity:
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.mapbox.maps.MapView
import com.mapbox.maps.Style
import com.mapbox.geojson.Point
class MapActivity : AppCompatActivity() {
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
mapView = findViewById(R.id.mapView)
mapView.mapboxMap.setCamera(
CameraOptions.Builder()
.center(Point.fromLngLat(-122.4194, 37.7749))
.zoom(12.0)
.build()
)
mapView.mapboxMap.loadStyle(Style.STANDARD)
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}
Point annotations are the most common way to mark locations on the map.
Jetpack Compose:
MapboxMap(modifier = Modifier.fillMaxSize()) {
MapEffect(Unit) { mapView ->
// Load style first
mapView.mapboxMap.loadStyle(Style.STANDARD)
// Create annotation manager and add markers
val annotationManager = mapView.annotations.createPointAnnotationManager()
val pointAnnotation = PointAnnotationOptions()
.withPoint(Point.fromLngLat(-122.4194, 37.7749))
.withIconImage("custom-marker")
annotationManager.create(pointAnnotation)
}
}
// Note: Compose doesn't have declarative PointAnnotation component
// Markers must be added imperatively via MapEffect
View System:
// Create annotation manager (once, reuse for updates)
val pointAnnotationManager = mapView.annotations.createPointAnnotationManager()
// Create marker
val pointAnnotation = PointAnnotationOptions()
.withPoint(Point.fromLngLat(-122.4194, 37.7749))
.withIconImage("custom-marker")
pointAnnotationManager.create(pointAnnotation)
Multiple markers:
val locations = listOf(
Point.fromLngLat(-122.4194, 37.7749),
Point.fromLngLat(-122.4094, 37.7849),
Point.fromLngLat(-122.4294, 37.7649)
)
val annotations = locations.map { point ->
PointAnnotationOptions()
.withPoint(point)
.withIconImage("marker")
}
pointAnnotationManager.create(annotations)
val circleAnnotationManager = mapView.annotations.createCircleAnnotationManager()
val circle = CircleAnnotationOptions()
.withPoint(Point.fromLngLat(-122.4194, 37.7749))
.withCircleRadius(10.0)
.withCircleColor("#FF0000")
circleAnnotationManager.create(circle)
val polylineAnnotationManager = mapView.annotations.createPolylineAnnotationManager()
val polyline = PolylineAnnotationOptions()
.withPoints(listOf(point1, point2, point3))
.withLineColor("#0000FF")
.withLineWidth(4.0)
polylineAnnotationManager.create(polyline)
val polygonAnnotationManager = mapView.annotations.createPolygonAnnotationManager()
val points = listOf(listOf(coord1, coord2, coord3, coord1)) // Close the polygon
val polygon = PolygonAnnotationOptions()
.withPoints(points)
.withFillColor("#0000FF")
.withFillOpacity(0.5)
polygonAnnotationManager.create(polygon)
Step 1: Add permissions to AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Step 2: Request permissions and show location:
// Request permissions first (use ActivityResultContracts)
// Show location puck
mapView.location.updateSettings {
enabled = true
puckBearingEnabled = true
}
To make the camera follow the user's location as they move:
class MapActivity : AppCompatActivity() {
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
mapView = findViewById(R.id.mapView)
mapView.mapboxMap.loadStyle(Style.STANDARD)
setupLocationTracking()
}
private fun setupLocationTracking() {
// Request permissions first (use ActivityResultContracts)
// Show user location
mapView.location.updateSettings {
enabled = true
puckBearingEnabled = true
}
// Follow user location with camera
mapView.location.addOnIndicatorPositionChangedListener { point ->
mapView.camera.easeTo(
CameraOptions.Builder()
.center(point)
.zoom(15.0)
.pitch(45.0)
.build(),
MapAnimationOptions.Builder()
.duration(1000)
.build()
)
}
// Optional: Follow bearing (direction) as well
mapView.location.addOnIndicatorBearingChangedListener { bearing ->
mapView.camera.easeTo(
CameraOptions.Builder()
.bearing(bearing)
.build(),
MapAnimationOptions.Builder()
.duration(1000)
.build()
)
}
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}
mapView.location.getLastLocation { location ->
location?.let {
val point = Point.fromLngLat(it.longitude, it.latitude)
mapView.camera.easeTo(
CameraOptions.Builder()
.center(point)
.zoom(14.0)
.build()
)
}
}
Add your own data to the map using GeoJSON sources and layers.
// Create coordinates for the line
val routeCoordinates = listOf(
Point.fromLngLat(-122.4194, 37.7749),
Point.fromLngLat(-122.4094, 37.7849),
Point.fromLngLat(-122.3994, 37.7949)
)
// Create GeoJSON source
val geoJsonSource = geoJsonSource("route-source") {
geometry(LineString.fromLngLats(routeCoordinates))
}
mapView.mapboxMap.style?.addSource(geoJsonSource)
// Create line layer
val lineLayer = lineLayer("route-layer", "route-source") {
lineColor(Color.BLUE)
lineWidth(4.0)
lineCap(LineCap.ROUND)
lineJoin(LineJoin.ROUND)
}
mapView.mapboxMap.style?.addLayer(lineLayer)
val polygonCoordinates = listOf(
listOf(coord1, coord2, coord3, coord1) // Close the polygon
)
val geoJsonSource = geoJsonSource("area-source") {
geometry(Polygon.fromLngLats(polygonCoordinates))
}
mapView.mapboxMap.style?.addSource(geoJsonSource)
val fillLayer = fillLayer("area-fill", "area-source") {
fillColor(Color.parseColor("#0000FF"))
fillOpacity(0.3)
fillOutlineColor(Color.parseColor("#0000FF"))
}
mapView.mapboxMap.style?.addLayer(fillLayer)
val geojsonString = """
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [-122.4194, 37.7749]},
"properties": {"name": "Location 1"}
},
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [-122.4094, 37.7849]},
"properties": {"name": "Location 2"}
}
]
}
"""
val geoJsonSource = geoJsonSource("points-source") {
data(geojsonString)
}
mapView.mapboxMap.style?.addSource(geoJsonSource)
val symbolLayer = symbolLayer("points-layer", "points-source") {
iconImage("marker")
textField(Expression.get("name"))
textOffset(listOf(0.0, 1.5))
}
mapView.mapboxMap.style?.addLayer(symbolLayer)
mapView.mapboxMap.style?.getLayerAs<LineLayer>("route-layer")?.let { layer ->
layer.lineColor(Color.RED)
layer.lineWidth(6.0)
}
mapView.mapboxMap.style?.removeStyleLayer("route-layer")
mapView.mapboxMap.style?.removeStyleSource("route-source")
// Compose - Update camera state
cameraState.position = CameraPosition(
center = Point.fromLngLat(-74.0060, 40.7128),
zoom = 14.0,
bearing = 90.0,
pitch = 60.0
)
// Views - Immediate
mapView.mapboxMap.setCamera(
CameraOptions.Builder()
.center(Point.fromLngLat(-74.0060, 40.7128))
.zoom(14.0)
.bearing(90.0)
.pitch(60.0)
.build()
)
// Fly animation (dramatic arc)
mapView.camera.flyTo(
CameraOptions.Builder()
.center(destination)
.zoom(15.0)
.build(),
MapAnimationOptions.Builder()
.duration(2000)
.build()
)
// Ease animation (smooth)
mapView.camera.easeTo(
CameraOptions.Builder()
.center(destination)
.zoom(15.0)
.build(),
MapAnimationOptions.Builder()
.duration(1000)
.build()
)
val coordinates = listOf(coord1, coord2, coord3)
val camera = mapView.mapboxMap.cameraForCoordinates(
coordinates,
EdgeInsets(50.0, 50.0, 50.0, 50.0),
bearing = 0.0,
pitch = 0.0
)
mapView.camera.easeTo(camera)
// Compose - load style via MapEffect
MapboxMap(modifier = Modifier.fillMaxSize()) {
MapEffect(Unit) { mapView ->
// Style.STANDARD loads by default, explicit loading only needed for other styles
// mapView.mapboxMap.loadStyle(Style.STREETS) // Mapbox Streets
// mapView.mapboxMap.loadStyle(Style.OUTDOORS) // Mapbox Outdoors
// mapView.mapboxMap.loadStyle(Style.LIGHT) // Mapbox Light
// mapView.mapboxMap.loadStyle(Style.DARK) // Mapbox Dark
// mapView.mapboxMap.loadStyle(Style.STANDARD_SATELLITE) // Satellite imagery
// mapView.mapboxMap.loadStyle(Style.SATELLITE_STREETS) // Satellite + streets
}
}
// Views
mapView.mapboxMap.loadStyle(Style.STANDARD)
mapView.mapboxMap.loadStyle(Style.DARK)
val customStyleUrl = "mapbox://styles/username/style-id"
// Compose
MapboxMap(modifier = Modifier.fillMaxSize()) {
MapEffect(Unit) { mapView ->
mapView.mapboxMap.loadStyle(customStyleUrl)
}
}
// Views
mapView.mapboxMap.loadStyle(customStyleUrl)
The modern Interactions API allows handling taps on map features with typed feature access. Works with Standard Style predefined featuresets like POIs, buildings, and place labels.
View System Pattern:
import com.mapbox.maps.interactions.ClickInteraction
class MapActivity : AppCompatActivity() {
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
mapView = findViewById(R.id.mapView)
mapView.mapboxMap.loadStyle(Style.STANDARD)
setupFeatureInteractions()
}
private fun setupFeatureInteractions() {
// Tap on POI features
mapView.mapboxMap.addInteraction(
ClickInteraction.standardPoi { poi, context ->
Log.d("MapTap", "Tapped POI: ${poi.name}")
true // Stop propagation
}
)
// Tap on buildings
mapView.mapboxMap.addInteraction(
ClickInteraction.standardBuildings { building, context ->
Log.d("MapTap", "Tapped building")
// Highlight the building
mapView.mapboxMap.setFeatureState(
building,
StandardBuildingsState {
highlight(true)
}
)
true
}
)
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}
Jetpack Compose Pattern:
@Composable
fun MapScreen() {
MapboxMap(modifier = Modifier.fillMaxSize()) {
MapEffect(Unit) { mapView ->
// Load Standard style
mapView.mapboxMap.loadStyle(Style.STANDARD)
// Add featureset interactions using View system API
mapView.mapboxMap.addInteraction(
ClickInteraction.standardPoi { poi, context ->
Log.d("MapTap", "Tapped POI: ${poi.name}")
true
}
)
mapView.mapboxMap.addInteraction(
ClickInteraction.standardBuildings { building, context ->
Log.d("MapTap", "Tapped building")
mapView.mapboxMap.setFeatureState(
building,
state = mapOf("select" to true)
)
true
}
)
}
}
}
// Note: Featureset interactions in Compose use MapEffect to access
// the underlying MapView and use the View system interaction API
### Tap on Custom Layers
```kotlin
mapView.mapboxMap.addInteraction(
ClickInteraction.layer("custom-layer-id") { feature, context ->
Log.d("MapTap", "Feature properties: ${feature.properties()}")
true
}
)
import com.mapbox.maps.interactions.LongClickInteraction
mapView.mapboxMap.addInteraction(
LongClickInteraction.standardPoi { poi, context ->
Log.d("MapTap", "Long pressed POI: ${poi.name}")
true
}
)
mapView.gestures.addOnMapClickListener { point ->
Log.d("MapClick", "Tapped at: ${point.latitude()}, ${point.longitude()}")
true // Consume event
}
// Disable specific gestures
mapView.gestures.pitchEnabled = false
mapView.gestures.rotateEnabled = false
// Configure zoom limits
mapView.mapboxMap.setCamera(
CameraOptions.Builder()
.zoom(12.0)
.build()
)
// ❌ Don't create new managers repeatedly
fun updateMarkers() {
val manager = mapView.annotations.createPointAnnotationManager()
manager.create(markers)
}
// ✅ Create once, reuse
val pointAnnotationManager = mapView.annotations.createPointAnnotationManager()
fun updateMarkers() {
pointAnnotationManager.deleteAll()
pointAnnotationManager.create(markers)
}
// ✅ Create all at once
pointAnnotationManager.create(allAnnotations)
// ❌ Don't create one by one
allAnnotations.forEach { annotation ->
pointAnnotationManager.create(annotation)
}
// Always call lifecycle methods
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
// ✅ Standard style is optimized and recommended
Style.STANDARD
// Use other styles only when needed for specific use cases
Style.STANDARD_SATELLITE // Satellite imagery
Check:
mapbox_access_token.xmlmapView.mapboxMap.subscribeStyleLoaded { _ ->
Log.d("Map", "Style loaded successfully")
// Add layers and sources here
}
Style.STANDARD (recommended and optimized)Weekly Installs
174
Repository
GitHub Stars
35
First Seen
Feb 1, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
gemini-cli154
opencode154
codex151
github-copilot149
cursor143
kimi-cli140
Kotlin Exposed ORM 模式指南:DSL查询、DAO、事务管理与生产配置
1,100 周安装
iOS应用本地化指南:Xcode字符串目录、SwiftUI/UIKit国际化与RTL支持
159 周安装
iOS Auto Layout 调试指南:快速解决约束冲突与布局错误
160 周安装
依赖审计完整指南:安全漏洞扫描、过时包检测与清理工作流
164 周安装
iOS Apple Intelligence 路由器使用指南 - Foundation Models 与 AI 方法分流
160 周安装
Agent Skills Creator 指南:创建遵循开放格式的AI技能,涵盖模式选择到验证
164 周安装
gentle-teaching 温和教学框架:AI辅助学习指南,培养独立解决问题能力
161 周安装