@@ -13,23 +13,38 @@ import androidx.activity.compose.setContent
1313import androidx.compose.foundation.layout.Box
1414import androidx.compose.foundation.layout.fillMaxSize
1515import androidx.compose.foundation.layout.padding
16+ import androidx.compose.material.icons.Icons
17+ import androidx.compose.material.icons.filled.LocationSearching
18+ import androidx.compose.material3.FloatingActionButton
19+ import androidx.compose.material3.Icon
1620import androidx.compose.material3.MaterialTheme
1721import androidx.compose.material3.Surface
18- import androidx.compose.material3.Text
1922import androidx.compose.runtime.Composable
2023import androidx.compose.runtime.getValue
2124import androidx.compose.runtime.mutableStateOf
2225import androidx.compose.runtime.remember
2326import androidx.compose.runtime.setValue
2427import androidx.compose.ui.Alignment
2528import androidx.compose.ui.Modifier
26- import androidx.compose.ui.platform.LocalContext
29+ import androidx.compose.ui.graphics.Color
2730import androidx.compose.ui.unit.dp
2831import androidx.compose.ui.viewinterop.AndroidView
32+ import androidx.lifecycle.compose.LocalLifecycleOwner
33+ import de.afarber.openmapview.CameraUpdateFactory
2934import de.afarber.openmapview.LatLng
35+ import de.afarber.openmapview.OnCameraMoveStartedListener
3036import de.afarber.openmapview.OpenMapView
31- import kotlin.math.roundToInt
37+ import de.afarber.openmapview.Polyline
3238
39+ /* *
40+ * Main activity demonstrating OpenMapView zoom controls.
41+ *
42+ * This example showcases:
43+ * - Custom zoom toolbar with +/- buttons
44+ * - Pinch-to-zoom gesture support
45+ * - Real-time zoom level display
46+ * - Camera state tracking
47+ */
3348class MainActivity : ComponentActivity () {
3449 override fun onCreate (savedInstanceState : Bundle ? ) {
3550 super .onCreate(savedInstanceState)
@@ -46,48 +61,127 @@ class MainActivity : ComponentActivity() {
4661 }
4762}
4863
64+ /* *
65+ * Main composable screen containing the map and zoom controls.
66+ *
67+ * Displays an OpenMapView with a status toolbar showing zoom level and camera state,
68+ * and a zoom toolbar for programmatic zoom control.
69+ * The map is centered on Bochum, Germany.
70+ */
4971@Composable
5072fun MapViewScreen () {
51- val context = LocalContext .current
52- val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner .current
53- var zoomLevel by remember { mutableStateOf(14.0f ) }
73+ val lifecycleOwner = LocalLifecycleOwner .current
74+
75+ // Irregular quadrilateral around Bochum (not a rectangle)
76+ val bochumCorners = listOf (
77+ LatLng (51.4873 , 7.2050 ), // North-West
78+ LatLng (51.4764 , 7.2159 ), // South-West
79+ LatLng (51.4824 , 7.2290 ), // South-East
80+ LatLng (51.4890 , 7.2166 ), // North-East
81+ )
82+
83+ // Initial location: center of the quadrilateral
84+ val initialLocation = LatLng (
85+ bochumCorners.map { it.latitude }.average(),
86+ bochumCorners.map { it.longitude }.average(),
87+ )
88+ val initialZoom = 14.0f
89+
90+ val bochumOutline = Polyline (
91+ points = bochumCorners + bochumCorners.first(), // Close the shape
92+ strokeColor = Color .Red ,
93+ strokeWidth = 4f ,
94+ )
95+
96+ // State variables
5497 var mapView: OpenMapView ? by remember { mutableStateOf(null ) }
98+ var zoomLevel by remember { mutableStateOf(" %.1f" .format(initialZoom)) }
99+ var cameraState by remember { mutableStateOf(" Idle" ) }
55100
56101 Box (modifier = Modifier .fillMaxSize()) {
102+ // Map view
57103 AndroidView (
58104 factory = { ctx ->
59105 OpenMapView (ctx).apply {
60- // Register lifecycle observer for proper cleanup
61106 lifecycleOwner.lifecycle.addObserver(this )
62107
63- setCenter(LatLng (51.4661 , 7.2491 )) // Bochum, Germany
64- setZoom(14.0f )
65- mapView = this
108+ setCenter(initialLocation)
109+ setZoom(initialZoom)
110+
111+ // Add the red polyline outline
112+ addPolyline(bochumOutline)
66113
67- // Enable built-in zoom controls
68- getUiSettings().isZoomControlsEnabled = true
114+ // Camera move started listener
115+ setOnCameraMoveStartedListener { reason ->
116+ cameraState = when (reason) {
117+ OnCameraMoveStartedListener .REASON_GESTURE -> " Moving (gesture)"
118+ OnCameraMoveStartedListener .REASON_API_ANIMATION -> " Moving (animation)"
119+ OnCameraMoveStartedListener .REASON_DEVELOPER_ANIMATION -> " Moving (programmatic)"
120+ else -> " Moving"
121+ }
122+ }
69123
70- // Add camera move listener to update zoom label
124+ // Camera move listener - updates zoom level during movement
71125 setOnCameraMoveListener {
72- zoomLevel = getZoom()
126+ zoomLevel = " %.1f" .format(getZoom())
127+ }
128+
129+ // Camera idle listener
130+ setOnCameraIdleListener {
131+ cameraState = " Idle"
132+ zoomLevel = " %.1f" .format(getZoom())
73133 }
134+
135+ mapView = this
74136 }
75137 },
76138 modifier = Modifier .fillMaxSize(),
77139 )
78140
79- // Zoom level title at the top
80- Surface (
141+ // Status overlay at top
142+ StatusToolbar (
143+ zoomLevel = zoomLevel,
144+ cameraState = cameraState,
81145 modifier = Modifier
82146 .align(Alignment .TopCenter )
83- .padding(top = 16 .dp),
84- color = MaterialTheme .colorScheme.surface.copy(alpha = 0.9f ),
85- shape = MaterialTheme .shapes.small,
147+ .padding(16 .dp),
148+ )
149+
150+ // Zoom toolbar at bottom
151+ ZoomToolbar (
152+ onZoomInClick = {
153+ mapView?.apply {
154+ setZoom(getZoom() + 1.0f )
155+ zoomLevel = " %.1f" .format(getZoom())
156+ }
157+ },
158+ onZoomOutClick = {
159+ mapView?.apply {
160+ setZoom(getZoom() - 1.0f )
161+ zoomLevel = " %.1f" .format(getZoom())
162+ }
163+ },
164+ modifier = Modifier
165+ .align(Alignment .BottomCenter )
166+ .padding(bottom = 16 .dp),
167+ )
168+
169+ FloatingActionButton (
170+ onClick = {
171+ mapView?.animateCamera(
172+ CameraUpdateFactory .newLatLngZoom(initialLocation, initialZoom),
173+ 500 ,
174+ )
175+ },
176+ containerColor = OsmHighwayPink ,
177+ contentColor = Color .Black ,
178+ modifier = Modifier
179+ .align(Alignment .BottomEnd )
180+ .padding(16 .dp),
86181 ) {
87- Text (
88- text = " Zoom: ${zoomLevel.roundToInt()} " ,
89- modifier = Modifier .padding(horizontal = 16 .dp, vertical = 12 .dp),
90- style = MaterialTheme .typography.titleMedium,
182+ Icon (
183+ imageVector = Icons .Default .LocationSearching ,
184+ contentDescription = " Reset" ,
91185 )
92186 }
93187 }
0 commit comments