Present on an Android Surface
AndroidMapPresentation presents a MapState on an
Android Surface without a Compose UI view. The same state can move between a
Surface and MaplibreMap. The
Android Auto demo
uses it in a car app.
Present the state
Section titled “Present the state”Create the state with MapRuntime.createMapState. Its base style,
style content, camera operations, and click handlers work as they do with
MaplibreMap. One presentation presents a state at a time. Close it before
presenting the state elsewhere.
Construct the presentation on the main thread with a Lifecycle. Rendering
pauses while the lifecycle is stopped.
/** Your code owns the MapState and each Surface. Call these methods on the main thread. */class SurfaceMapHost(context: Context, state: MapState, lifecycle: Lifecycle) : AutoCloseable { val presentation = AndroidMapPresentation(context, state, lifecycle) private var binding: AndroidMapPresentation.SurfaceBinding? = null
fun onSurfaceAvailable(surface: Surface, width: Int, height: Int, density: Float) { binding = presentation.attachSurface(surface, width, height, density) }
fun onSurfaceChanged(width: Int, height: Int, density: Float) { binding?.update(width, height, density) }
fun onSurfaceDestroyed() { binding?.close() binding = null }
fun onConfigurationChanged(configuration: Configuration) { presentation.updateConfiguration(configuration) }
override fun close() { presentation.close() }}attachSurface takes the Surface’s size in physical pixels and its density in
physical pixels per logical pixel. Keep the returned
AndroidMapPresentation.SurfaceBinding to update the size or density,
and close it before releasing the Surface.
Forward configuration changes with updateConfiguration so the map follows
the host’s locale, font scale, layout direction, and theme. See
AndroidMapPresentation for presentation lifecycle and failure handling.
Pass gestures
Section titled “Pass gestures”The presentation has no gesture recognizer. Pass gestures your code recognized
to MapState.panBy, MapState.fling,
MapState.scaleBy, and MapState.click. They follow the
camera permissions and callbacks in the presentation’s interactions.
Convert physical pixels to logical pixels first.
/** Converts physical pixels to the map's logical pixels before passing gestures. */class SurfaceInput(private val state: MapState, private val density: Float) { fun pan(deltaX: Float, deltaY: Float) { state.panBy(logical(deltaX, deltaY)) }
fun fling(velocityX: Float, velocityY: Float) { state.fling(logical(velocityX, velocityY)) }
fun scale(factor: Float, focusX: Float?, focusY: Float?) { val anchor = if (focusX != null && focusY != null) logical(focusX, focusY) else null state.scaleBy(factor.toDouble(), anchor) }
fun click(x: Float, y: Float) { state.click(logical(x, y)) }
private fun logical(x: Float, y: Float) = DpOffset((x / density).dp, (y / density).dp)}