A modern, Kotlin-first MapView replacement for Android - powered by OpenStreetMap.
Add to your build.gradle.kts:
dependencies {
implementation("de.afarber:openmapview:0.18.0")
// Optional: the OpenMap composable for Jetpack Compose
implementation("de.afarber:openmapview-compose:0.18.0")
}Requirements: minSdk 23, compileSdk 37 and Android Gradle Plugin 9.1 or newer (since 0.14.0, because androidx.core 1.19 and Compose UI 1.12 require them). Since 0.14.1 the app also needs Kotlin 2.3 or newer, because the library is built with Kotlin 2.4. Projects still on compileSdk 36 or AGP 8 can stay on 0.13.3.
The library is available on Maven Central. Alternative distribution via JitPack is also supported.
OpenMapView is MIT licensed, so you can use it in commercial apps. Its four runtime dependencies (androidx core-ktx, lifecycle-runtime-ktx, Compose ui-graphics and ktor-client-android) are all Apache 2.0. See the Dependency License Report for details.
- Drop-in compatible with Google
MapView(non-deprecated methods only) - Lightweight, pure Kotlin implementation with zero Google dependencies
- OSM tiles via standard APIs (free, no API keys required)
- Smooth camera animations with customizable durations and callbacks
- Multiple overlay types: Markers, Polylines, Polygons, Circles, Ground Overlays, Tile Overlays, Info Windows
- Multiple map types (see Map Types Guide)
- Built-in zoom controls (+/- buttons) via UiSettings
- External app integration (open in Google Maps, OsmAnd, etc. via geo: URI)
- GeoJSON import support
- Extensible marker, overlay, and gesture handling
Full API reference documentation is available at afarber.github.io/OpenMapView.
The documentation is generated from KDoc comments and redeployed whenever the library sources change on main.
Explore the example applications to see OpenMapView in action:
- Example01Pan - Basic map panning with touch gestures
- Example02Zoom - Built-in zoom controls (+/- buttons) and zoom level display
- Example03Markers - Marker overlays with custom colors and info windows
- Example04Polylines - Polylines, polygons, and polygons with holes (donut shapes)
- Example05Camera - Camera animations with callbacks, custom durations, and built-in zoom controls
- Example06Clicks - Map click and long-click listeners with coordinate display
- Example07DraggableMarkers - Draggable markers with drag event listeners
- Example08Circles - Circles with radius, styling, z-index ordering, and click listeners
- Example09Overlays - Tile overlays with transparency control (OpenSeaMap, Railways, Trails, Snow)
- Example10GroundOverlays - Ground overlays with position/bounds modes, rotation, and transparency
- Example11MapTypes - Switching between all 15 map types (OSM, CyclOSM, Thunderforest, CARTO, Stamen and more)
- Example12Toolbar - Open location in external map apps via geo: URI with OpenStreetMap browser fallback
- Example13Compose - The
OpenMapcomposable in a tab, keeping its camera while other tabs are shown
- Public API Compatibility - Google MapView API implementation status and compatibility matrix
- Architecture - Architectural design and comparison with Google Maps SDK
- Replacing Google Maps - Migration guide for switching from Google Maps SDK to OpenMapView
- Map Types - Available map types and tile sources
- Map Toolbar - External map app integration using geo: URIs with browser fallback
- Performance - Performance optimization and benchmarking
- Contributing Guide - Code quality requirements, formatting, git hooks, and PR process
- Lifecycle Management - How OpenMapView handles Android lifecycle events
- Publishing Guide - Publishing to Maven Central and JitPack
- GitHub Workflows - CI/CD pipeline and workflow architecture
- Unit Testing - JVM unit tests with Robolectric for Android framework APIs
- Instrumented Testing - On-device (phone and auto) testing with Android emulator
The optional openmapview-compose artifact provides the OpenMap composable. Its camera is kept in a CameraPositionState, which survives configuration changes and, when hoisted above the screen, also switching tabs:
@Composable
fun MapScreen() {
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition(LatLng(51.4661, 7.2491), 14f)
}
OpenMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState,
onMapCreated = { map ->
map.addMarker(Marker(position = LatLng(51.4661, 7.2491), title = "Bochum City Center"))
},
)
// Move the camera by setting cameraPositionState.position, or animate it
// from a coroutine: cameraPositionState.animate(CameraUpdateFactory.zoomIn())
}Without that artifact, show OpenMapView in an AndroidView:
@Composable
fun MapViewScreen() {
AndroidView(
factory = { context ->
// Cleans up after itself when it leaves the screen, no lifecycle code needed
OpenMapView(context).apply {
setCenter(LatLng(51.4661, 7.2491))
setZoom(14.0f)
// Add markers - Kotlin style (direct instantiation)
addMarker(Marker(
position = LatLng(51.4661, 7.2491),
title = "Bochum City Center"
))
// Or Google Maps API style (builder pattern)
addMarker(
MarkerOptions()
.position(LatLng(51.4700, 7.2550))
.title("North Location")
.draggable(true)
)
}
},
modifier = Modifier.fillMaxSize()
)
}class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val mapView = findViewById<OpenMapView>(R.id.mapView)
mapView.setZoom(14.0f)
mapView.setCenter(LatLng(51.4661, 7.2491))
}
}