Skip to content

Tutorial

En este tutorial aprenderás a usar MapConductor Android SDK para mostrar un mapa, añadir marcadores y figuras, y manejar interacciones de usuario.

  • Cómo instalar y configurar el SDK de MapConductor
  • Cómo mostrar un mapa
  • Cómo añadir marcadores, círculos y polilíneas
  • Cómo manejar eventos de tap/clic
  • Cómo controlar la posición de la cámara
  • Cómo cambiar entre distintos SDK de mapas
  • Android Studio instalado
  • Conocimientos básicos de Jetpack Compose
  • Conocimientos básicos de Kotlin

Añade las dependencias de MapConductor en build.gradle.kts o build.gradle del módulo:

dependencies {
val mapconductorVersion = "1.1.3"
// Usar BOM para unificar versiones
implementation(platform("com.mapconductor:mapconductor-bom:$mapconductorVersion"))
// Módulo principal (requerido)
implementation("com.mapconductor:core")
// Si utilizas Google Maps
implementation("com.mapconductor:for-googlemaps")
// O si utilizas Mapbox
// implementation("com.mapconductor:for-mapbox")
// O si utilizas HERE Maps
// implementation("com.mapconductor:for-here")
// O si utilizas ArcGIS
// implementation("com.mapconductor:for-arcgis")
// O si utilizas MapLibre
// implementation("com.mapconductor:for-maplibre")
}

Añade la siguiente configuración en build.gradle.kts o build.gradle:

plugin {
id("org.jetbrains.kotlin.plugin.compose:1.9.25")
id("org.jetbrains.kotlin.android:1.9.25")
}
android {
compileSdk = 35
defaultConfig {
minSdk = 26
targetSdk = 35
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion =
project.property("1.9.25").toString()
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
}

Importante: MapConductor proporciona una capa de API unificada sobre los SDK de mapas existentes. Antes de usar MapConductor, debes configurar cada SDK de mapas individualmente.

Cada SDK de mapas requiere sus propias claves API, permisos y configuración:

Empecemos con la forma más simple de mostrar un mapa centrado en Tokio.

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.mapconductor.core.GeoPoint
import com.mapconductor.core.MapCameraPosition
import com.mapconductor.googlemaps.GoogleMapView
import com.mapconductor.googlemaps.rememberGoogleMapViewState
@Composable
fun MyFirstMap(modifier: Modifier = Modifier) {
// Coordenadas de Tokio
val tokyo = GeoPoint.fromLatLong(35.6812, 139.7671)
// Posición inicial de la cámara
val initialCamera = MapCameraPosition(
position = tokyo,
zoom = 12
)
// Gestión de estado
val mapViewState = rememberGoogleMapViewState(
cameraPosition = initialCamera
)
// Mostrar mapa
GoogleMapView(
modifier = modifier,
state = mapViewState
)
}

Resultado del mapa Nota: El estilo de demostración se ha modificado del predeterminado para mayor claridad

¡Eso es! Ahora tienes un mapa centrado en Tokio mostrado en tu aplicación.

Añadamos marcadores al mapa.

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.mapconductor.core.*
import com.mapconductor.googlemaps.GoogleMapView
import com.mapconductor.googlemaps.rememberGoogleMapViewState
@Composable
fun MapWithMarkers(modifier: Modifier = Modifier) {
// Coordenadas
val marker1 = GeoPoint.fromLatLong(35.6586, 139.7454)
val marker2 = GeoPoint.fromLatLong(35.7101, 139.8107)
// Posición inicial
val initialCamera = MapCameraPosition(
position = GeoPoint.fromLatLong(35.6586, 139.7454),
zoom = 11
)
val mapViewState = rememberGoogleMapViewState(
cameraPosition = initialCamera
)
val onMarkerClick: (MarkerState) -> Unit = { markerState ->
println("Manejar clic en marcador${markerState.extra}")
}
GoogleMapView(
modifier = modifier.fillMaxSize(),
state = mapViewState
) {
// Añadir marcadores
Marker(
position = marker1,
icon = DefaultIcon(label = "Tokyo Tower"),
extra = "marker1",
onClick = onMarkerClick
)
Marker(
position = marker2,
icon = DefaultIcon(label = "Sky Tree"),
extra = "marker2",
onClick = onMarkerClick
)
}
}

Mapa con marcadores Nota: El estilo de demostración se ha modificado del predeterminado para mayor claridad

Puedes personalizar marcadores con colores y estilos:

Marker(
position = GeoPoint.fromLatLong(35.6586, 139.7454),
icon = DefaultIcon(
label = "TT",
backgroundColor = Color.Red,
textColor = Color.White
),
extra = "tokyo_tower",
anchor = Offset(0.5f, 1.0f) // Información de anclaje
)

Dibujemos un círculo alrededor de un marcador.

import androidx.compose.ui.graphics.Color
GoogleMapView(
modifier = modifier.fillMaxSize(),
state = mapViewState
) {
// Marcador
Marker(
position = GeoPoint.fromLatLong(35.6586, 139.7454),
icon = DefaultIcon(label = "TT"),
extra = "tokyo_tower"
)
// Dibujar círculo
Circle(
center = GeoPoint.fromLatLong(35.6586, 139.7454),
radiusMeters = 1000, // Radio (metros)
strokeColor = Color.Blue,
strokeWidth = 3.dp, // Ancho de trazo (dp)
fillColor = Color.Blue.copy(alpha = 0.2f)
)
}

Marcador con círculo Nota: El estilo de demostración se ha modificado del predeterminado para mayor claridad

Ahora dibujemos una línea que conecte dos puntos.

GoogleMapView(
modifier = modifier.fillMaxSize(),
state = mapViewState
) {
// Marcadores
Marker(position = GeoPoint.fromLatLong(35.6586, 139.7454), icon = DefaultIcon(label = "TT"))
Marker(position = GeoPoint.fromLatLong(35.7101, 139.8107), icon = DefaultIcon(label = "ST"))
// Dibujar línea
Polyline(
points = listOf(
GeoPoint.fromLatLong(35.6586, 139.7454),
GeoPoint.fromLatLong(35.7101, 139.8107)
),
strokeColor = Color.Red,
strokeWidth = 5.dp
)
}

Marcador con polilínea Nota: El estilo de demostración se ha modificado del predeterminado para mayor claridad

Puedes manejar cuándo los usuarios tocan el mapa:

import androidx.compose.runtime.*
@Composable
fun InteractiveMap(modifier: Modifier = Modifier) {
var clickedPosition by remember { mutableStateOf<GeoPointInterface?>(null) }
val initialCamera = MapCameraPosition(
position = GeoPoint.fromLatLong(35.6812, 139.7671),
zoom = 12
)
val mapViewState = rememberGoogleMapViewState(
cameraPosition = initialCamera
)
GoogleMapView(
modifier = modifier.fillMaxSize(),
state = mapViewState,
onMapClick = { geoPoint ->
// Mapa tocado
clickedPosition = geoPoint
println("Posición tocada${geoPoint.latitude}, ${geoPoint.longitude}")
}
) {
// Mostrar marcador
clickedPosition?.let { position ->
Marker(
position = position,
icon = DefaultIcon(label = "!"),
extra = "clicked_marker"
)
}
}
}

Nota: El estilo de demostración se ha modificado del predeterminado para mayor claridad

Maneja clics en marcadores y dispara animaciones:

val marker1 = GeoPoint.fromLatLong(35.6586, 139.7454)
val marker2 = GeoPoint.fromLatLong(35.7101, 139.8107)
GoogleMapView(
modifier = modifier.fillMaxSize(),
state = mapViewState
) {
Marker(
position = marker1,
icon = DefaultIcon(label = "TT"),
extra = "marker1",
onClick = { markerState ->
// Manejar clic en marcador
markerState.animate(MarkerAnimation.Drop)
}
)
Marker(
position = marker2,
icon = DefaultIcon(label = "ST"),
extra = "marker2",
onClick = { markerState ->
// Manejar clic en marcador
markerState.animate(MarkerAnimation.Bounce)
}
)
}

Nota: El estilo de demostración se ha modificado del predeterminado para mayor claridad

6-1. Mover la cámara a una ubicación específica

Section titled “6-1. Mover la cámara a una ubicación específica”

Usa botones para mover la cámara a diferentes ubicaciones:

import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.rememberCoroutineScope
import kotlinx.coroutines.launch
@Composable
fun MapWithCameraControl(modifier: Modifier = Modifier) {
val marker1 = GeoPoint.fromLatLong(35.6586, 139.7454)
val marker2 = GeoPoint.fromLatLong(35.7101, 139.8107)
val mapViewState = rememberGoogleMapViewState(
cameraPosition = MapCameraPosition(
position = marker1,
zoom = 12.0
)
)
val scope = rememberCoroutineScope()
Column(modifier = modifier.fillMaxSize()) {
// Botones
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Button(onClick = {
scope.launch {
mapViewState.moveCameraTo(
MapCameraPosition(
position = marker1,
zoom = 15
),
durationMillis = 1000
)
}
}) {
Text("Tokyo Tower")
}
Button(onClick = {
scope.launch {
mapViewState.moveCameraTo(
MapCameraPosition(
position = marker2,
zoom = 15
),
durationMillis = 1000
)
}
}) {
Text("Sky Tree")
}
}
// 地図
GoogleMapView(
modifier = Modifier.fillMaxSize(),
state = mapViewState
) {
Marker(position = marker1, icon = DefaultIcon(label = "TT"))
Marker(position = marker2, icon = DefaultIcon(label = "ST"))
}
}
}

6-2. Monitorear eventos de movimiento de cámara

Section titled “6-2. Monitorear eventos de movimiento de cámara”

Escucha eventos de movimiento de cámara:

GoogleMapView(
modifier = modifier.fillMaxSize(),
state = mapViewState,
onCameraMoveStart = {
println("Movimiento de cámara iniciado")
},
onCameraMove = { cameraPosition ->
println("Posición de cámara: ${cameraPosition.position.latitude}, ${cameraPosition.position.longitude}")
},
onCameraMoveEnd = {
println("Movimiento de cámara finalizado")
}
) {
// Marcadores etc.
}

La ventaja más grande de MapConductor es la capacidad de cambiar proveedores de mapas con cambios mínimos de código.

import com.mapconductor.googlemaps.GoogleMapView
import com.mapconductor.googlemaps.rememberGoogleMapViewState
@Composable
fun MyMap() {
val mapViewState = rememberGoogleMapViewState(
cameraPosition = initialCamera
)
GoogleMapView(
state = mapViewState
) {
// Marcadores y otros componentes
}
}

¡Los marcadores, círculos, polilíneas y otros componentes funcionan con exactamente el mismo código!

SDK de mapasMapViewStateInterfaceMapView
Google MapsrememberGoogleMapViewStateGoogleMapView
MapboxrememberMapboxViewStateMapboxMapView
HERE MapsrememberHereMapViewStateHereMapView
ArcGISrememberArcGISMapViewStateArcGISMapView
MapLibrerememberMapLibreViewStateMapLibreMapView

Aquí está el código de ejemplo completo que combina todo lo que has aprendido:

import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.mapconductor.core.*
import com.mapconductor.googlemaps.GoogleMapView
import com.mapconductor.googlemaps.rememberGoogleMapViewState
import kotlinx.coroutines.launch
@Composable
fun CompleteSample(modifier: Modifier = Modifier) {
val context = LocalContext.current
// Información de posición
val marker1 = GeoPoint.fromLatLong(35.6586, 139.7454)
val marker2 = GeoPoint.fromLatLong(35.7101, 139.8107)
// Guardar posición tocada
var clickedPosition by remember { mutableStateOf<GeoPointInterface?>(null) }
val initialCamera = MapCameraPosition(
position = GeoPoint.fromLatLong(35.6812, 139.7671),
zoom = 12.0
)
val mapViewState = rememberGoogleMapViewState(
cameraPosition = initialCamera
)
val scope = rememberCoroutineScope()
val onMarkerClick: (MarkerState) -> Unit = { markerState ->
Toast
.makeText(
context,
"clicked: ${markerState.extra}",
Toast.LENGTH_SHORT,
)
.show()
}
Column(modifier = modifier.fillMaxSize()) {
// Botones de control
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Button(onClick = {
scope.launch {
mapViewState.moveCameraTo(
MapCameraPosition(position = marker1, zoom = 15.0),
durationMillis = 1000
)
}
}) {
Text("TT")
}
Button(onClick = {
scope.launch {
mapViewState.moveCameraTo(
MapCameraPosition(position = marker2, zoom = 15.0),
durationMillis = 1000
)
}
}) {
Text("ST")
}
}
// Mapa
GoogleMapView(
modifier = Modifier.fillMaxSize(),
state = mapViewState,
onMapClick = { geoPoint ->
clickedPosition = geoPoint
}
) {
// Marcadores
Marker(
position = marker1,
icon = DefaultIcon(
label = "TT",
fillColor = Color.Red,
),
extra = "marker1",
onClick = onMarkerClick
)
Marker(
position = marker2,
icon = DefaultIcon(
label = "ST",
fillColor = Color.Blue
),
extra = "marker2",
onClick = onMarkerClick
)
// Marcador para posición tocada
clickedPosition?.let { position ->
Marker(
position = position,
icon = DefaultIcon(
label = "!",
fillColor = Color.Green
),
extra = "clicked",
onClick = onMarkerClick
)
}
// Círculo
Circle(
center = marker1,
radiusMeters = 1000,
strokeColor = Color.Red,
fillColor = Color.Red.copy(alpha = 0.2f)
)
Circle(
center = marker2,
radiusMeters = 1000,
strokeColor = Color.Blue,
fillColor = Color.Blue.copy(alpha = 0.2f)
)
// Polilínea
Polyline(
points = listOf(marker1, marker2),
strokeColor = Color.Magenta,
strokeWidth = 3.dp
)
}
}
}

Has aprendido los conceptos básicos del SDK de MapConductor. Para aprender más, consulta:

  • Verifica que tu clave API esté configurada correctamente
  • Comprueba tu conexión a Internet
  • Verifica que los permisos requeridos estén añadidos en AndroidManifest.xml
  • Verifica que minSdk esté configurado en 26 o superior
  • Comprueba que las versiones de Kotlin y Compose son compatibles
  • Verifica que las dependencias se hayan añadido correctamente
  • Verifica que MarkerState.onClick esté configurado
  • Asegúrate de que los marcadores tengan la propiedad extra configurada