Skip to content

Commit 8bcee17

Browse files
authored
Improve Example03Markers with navigation and info window features (#13)
* Improve Example03Markers with reusable toolbar components * Update README.md * Remove the red polyline * Add a separate MarkerData.kt file * Add info window toggle on marker tap and optional auto-dismiss timer * Just navigate between the markers instead of adding/removing them * Display the selected index and move the status toolbar to the left * Add marker info window auto-dismiss support via Marker.showInfoWindow() with internal mapView reference * Add FAB on the bottom right corner * Toggle info window when FAB is clicked * Implement setOnInfoWindowCloseListener * Fix the red Text * Update version; improve comments * Rename list to poiMarkers, improve SSOT * Add InfoWindowAutoDismissDuration * Rename Colors to Constants
1 parent b8e2bb3 commit 8bcee17

21 files changed

Lines changed: 783 additions & 159 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Add to your `build.gradle.kts`:
1414

1515
```kotlin
1616
dependencies {
17-
implementation("de.afarber:openmapview:0.11.0")
17+
implementation("de.afarber:openmapview:0.12.0")
1818
}
1919
```
2020

docs/PERFORMANCE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ HttpClient(Android) {
269269
All requests include a user-agent header as required by OSM tile usage policy:
270270

271271
```kotlin
272-
header("User-Agent", "OpenMapView/0.11.0 (https://github.com/afarber/OpenMapView)")
272+
header("User-Agent", "OpenMapView/0.12.0 (https://github.com/afarber/OpenMapView)")
273273
```
274274

275275
### Coroutine-Based Downloads

docs/PUBLIC_API.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,18 @@ This document lists all public non-deprecated methods from Google's MapView and
4343
| -------------------------- | ----------- | ----- |
4444
| `addMarker(MarkerOptions)` | `Marker` | |
4545
| `clear()` | `void` | |
46+
| `showInfoWindow(Marker)` | `void` | Shows marker's info window with auto-dismiss support |
47+
| `hideInfoWindow(Marker)` | `void` | Hides marker's info window and cancels auto-dismiss |
48+
49+
---
50+
51+
## Marker Class
52+
53+
| Method | Return Type | Notes |
54+
| ------------------ | ----------- | -------------------------------------------------------------------- |
55+
| `showInfoWindow()` | `void` | Shows this marker's info window (auto-dismiss if configured via UiSettings) |
56+
| `hideInfoWindow()` | `void` | Hides this marker's info window |
57+
| `isInfoWindowShown`| `Boolean` | Returns whether this marker's info window is currently shown |
4658

4759
---
4860

@@ -222,6 +234,7 @@ Methods available on the UiSettings object returned by `getUiSettings()`:
222234
| `setMapToolbarEnabled(boolean)` | `void` | Not implemented - use openInExternalApp() instead (see External Map Integration section) |
223235
| `isMapToolbarEnabled()` | `boolean` | Always returns false |
224236
| `setAllGesturesEnabled(boolean)` | `void` | |
237+
| `infoWindowAutoDismiss` | `Duration` | OpenMapView-specific: auto-dismiss info windows after duration (ZERO = disabled) |
225238

226239
---
227240

@@ -287,7 +300,7 @@ OpenMapView provides comprehensive event listener support using Kotlin `fun inte
287300
| `setOnMapLoadedCallback(OnMapLoadedCallback)` | `void` | Not implemented - tiles load asynchronously, callback could be added |
288301
| `setInfoWindowAdapter(InfoWindowAdapter)` | `void` | Not implemented - custom adapters not yet implemented |
289302
| `setOnInfoWindowClickListener(OnInfoWindowClickListener)` | `void` | |
290-
| `setOnInfoWindowCloseListener(OnInfoWindowCloseListener)` | `void` | Not implemented |
303+
| `setOnInfoWindowCloseListener(OnInfoWindowCloseListener)` | `void` | Called when info window is closed (manual or auto-dismiss) |
291304
| `setOnInfoWindowLongClickListener(OnInfoWindowLongClickListener)` | `void` | Not implemented |
292305
| `setOnMyLocationButtonClickListener(OnMyLocationButtonClickListener)` | `void` | Not implemented |
293306
| `setOnMyLocationClickListener(OnMyLocationClickListener)` | `void` | Not implemented |

docs/REPLACING_GOOGLE_MAPS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ dependencies {
107107
```kotlin
108108
// Add to build.gradle.kts
109109
dependencies {
110-
implementation("de.afarber:openmapview:0.11.0")
110+
implementation("de.afarber:openmapview:0.12.0")
111111
}
112112
```
113113

examples/Example01Pan/src/main/kotlin/de/afarber/openmapview/example01pan/ArrowToolbar.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ fun ArrowToolbar(
9090
modifier = Modifier.size(56.dp),
9191
shape = RoundedCornerShape(topEnd = ToolbarCornerRadius, bottomEnd = ToolbarCornerRadius),
9292
colors = IconButtonDefaults.filledIconButtonColors(
93-
containerColor = OsmParkGreen,
93+
containerColor = OsmHighwayPink,
9494
contentColor = Color.Black,
9595
),
9696
) {

examples/Example02Zoom/src/main/kotlin/de/afarber/openmapview/example02zoom/ZoomToolbar.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import androidx.compose.foundation.layout.Row
1111
import androidx.compose.foundation.layout.size
1212
import androidx.compose.foundation.shape.RoundedCornerShape
1313
import androidx.compose.material.icons.Icons
14-
import androidx.compose.material.icons.filled.Add
15-
import androidx.compose.material.icons.filled.Remove
14+
import androidx.compose.material.icons.filled.ZoomIn
15+
import androidx.compose.material.icons.filled.ZoomOut
1616
import androidx.compose.material3.FilledIconButton
1717
import androidx.compose.material3.Icon
1818
import androidx.compose.material3.IconButtonDefaults
@@ -54,7 +54,7 @@ fun ZoomToolbar(
5454
contentColor = Color.Black,
5555
),
5656
) {
57-
Icon(Icons.Default.Add, contentDescription = "Zoom In")
57+
Icon(Icons.Default.ZoomIn, contentDescription = "Zoom In")
5858
}
5959
FilledIconButton(
6060
onClick = onZoomOutClick,
@@ -65,7 +65,7 @@ fun ZoomToolbar(
6565
contentColor = Color.Black,
6666
),
6767
) {
68-
Icon(Icons.Default.Remove, contentDescription = "Zoom Out")
68+
Icon(Icons.Default.ZoomOut, contentDescription = "Zoom Out")
6969
}
7070
}
7171
}

examples/Example03Markers/README.md

Lines changed: 96 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
# Example03Markers - Marker Overlays and Click Handling
1+
# Example03Markers - Marker Navigation and Info Windows
22

33
[Back to README](../../README.md)
44

5-
This example demonstrates the marker system in OpenMapView, including marker rendering, touch detection, and click event handling.
5+
This example demonstrates the marker system in OpenMapView, including marker rendering, touch detection, info windows, and marker navigation.
66

77
## Features Demonstrated
88

9-
- Multiple markers at different geographic locations
10-
- Default red teardrop marker icons with color variations
11-
- Both API styles: Kotlin direct instantiation and Google Maps builder pattern
12-
- Marker click detection and callbacks
13-
- Toast notifications on marker click
14-
- Markers with title and snippet metadata
15-
- Marker positioning with proper anchor points
16-
- Markers that stay fixed during pan and zoom
17-
- Info windows showing marker titles and snippets
9+
- Multiple markers at real Bochum landmark locations
10+
- Default teardrop marker icons with color variations
11+
- Marker click detection and selection tracking
12+
- Info windows showing marker titles and snippets with auto-dismiss
13+
- Navigation between markers with prev/next buttons
14+
- Info window toggle via FAB or marker tap
15+
- Real-time status display (selection index, camera state)
16+
- Visual feedback when info window is shown (red text)
1817

1918
## Screenshot
2019

@@ -39,30 +38,75 @@ This example demonstrates the marker system in OpenMapView, including marker ren
3938
adb shell am start -n de.afarber.openmapview.example03markers/.MainActivity
4039
```
4140

41+
## Project Structure
42+
43+
```
44+
example03markers/
45+
├── MainActivity.kt # Main activity and MapViewScreen composable
46+
├── MarkerToolbar.kt # Horizontal toolbar with prev/next navigation buttons
47+
├── StatusToolbar.kt # Status overlay showing selection index and camera state
48+
├── MarkerData.kt # Marker data class and Bochum POI locations
49+
└── Constants.kt # Colors, dimensions, and durations
50+
```
51+
4252
## Code Highlights
4353

44-
### Adding Markers - Kotlin Style
54+
### MainActivity.kt
4555

4656
```kotlin
47-
OpenMapView(context).apply {
48-
setCenter(LatLng(51.4661, 7.2491)) // Bochum, Germany
49-
setZoom(14.0)
50-
51-
// Kotlin-style direct instantiation
52-
addMarker(
53-
Marker(
54-
position = LatLng(51.4661, 7.2491),
55-
title = "Bochum City Center",
56-
snippet = "Welcome to Bochum!",
57+
@Composable
58+
fun MapViewScreen() {
59+
val lifecycleOwner = LocalLifecycleOwner.current
60+
var mapView: OpenMapView? by remember { mutableStateOf(null) }
61+
var selectedIndex by remember { mutableIntStateOf(0) }
62+
var selectedMarker: Marker? by remember { mutableStateOf(null) }
63+
var isInfoWindowShown by remember { mutableStateOf(false) }
64+
65+
Box(modifier = Modifier.fillMaxSize()) {
66+
AndroidView(
67+
factory = { ctx ->
68+
OpenMapView(ctx).apply {
69+
lifecycleOwner.lifecycle.addObserver(this)
70+
setCenter(initialLocation)
71+
setZoom(13.0f)
72+
getUiSettings().infoWindowAutoDismiss = 10.seconds
73+
74+
setOnMarkerClickListener { marker ->
75+
selectedMarker = marker
76+
isInfoWindowShown = marker.isInfoWindowShown
77+
true
78+
}
79+
setOnInfoWindowCloseListener {
80+
isInfoWindowShown = false
81+
}
82+
mapView = this
83+
}
84+
},
85+
modifier = Modifier.fillMaxSize(),
5786
)
58-
)
87+
88+
StatusToolbar(selectedIndex, selectedMarker?.title, cameraState, isInfoWindowShown, ...)
89+
MarkerToolbar(onPrevClick = { ... }, onNextClick = { ... })
90+
}
5991
}
6092
```
6193

94+
### Adding Markers - Kotlin Style
95+
96+
```kotlin
97+
addMarker(
98+
Marker(
99+
position = LatLng(51.4783, 7.2231),
100+
title = "Bochum Hauptbahnhof",
101+
snippet = "Main railway station",
102+
icon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED),
103+
)
104+
)
105+
```
106+
62107
### Adding Markers - Google Maps Style
63108

64109
```kotlin
65-
// Google Maps API builder pattern
66110
addMarker(
67111
MarkerOptions()
68112
.position(LatLng(51.4650, 7.2500))
@@ -72,57 +116,51 @@ addMarker(
72116
)
73117
```
74118

75-
### Click Listener
119+
### OSM-Inspired Colors (Constants.kt)
76120

77121
```kotlin
78-
setOnMarkerClickListener { marker ->
79-
val message = buildString {
80-
append(marker.title ?: "Marker")
81-
if (marker.snippet != null) {
82-
append("\n")
83-
append(marker.snippet)
84-
}
85-
}
86-
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
87-
true // Consume the click event
88-
}
122+
val OsmParkGreen = Color(0xFFAAD3A2) // Navigation buttons (prev/next)
123+
val OsmHighwayPink = Color(0xFFE892A2) // Info window toggle FAB
124+
val OsmWaterBlue = Color(0xFFAAD3DF) // Reserved for future use
89125
```
90126

91127
### Key Concepts
92128

93129
- **Marker**: Data class with position, title, snippet, icon, anchor, and tag
94130
- **addMarker()**: Add a marker to the map
95-
- **removeMarker()**: Remove a specific marker
96-
- **clearMarkers()**: Remove all markers
131+
- **getMarkers()**: Get list of all markers
97132
- **setOnMarkerClickListener()**: Handle marker click events
98-
- **Default icon**: Red teardrop shape generated via MarkerIconFactory
99-
- **Custom icons**: Provide your own Bitmap via the `icon` parameter
133+
- **setOnInfoWindowClickListener()**: Handle info window click events
134+
- **setOnInfoWindowCloseListener()**: Handle info window close events (manual or auto-dismiss)
135+
- **infoWindowAutoDismiss**: Auto-dismiss info windows after a duration
100136

101137
## What to Test
102138

103-
1. **Launch the app** - you should see 5 red markers around Bochum
104-
2. **Click a marker** - Toast message shows title and snippet
105-
3. **Pan the map** - markers stay at correct geographic positions
106-
4. **Zoom in/out** - markers remain properly positioned
107-
5. **Click different markers** - each shows its own title/snippet
139+
1. **Launch the app** - you should see 6 colored markers at Bochum landmarks
140+
2. **Tap a marker** - info window shows title and snippet, status text turns red
141+
3. **Tap the same marker again** - info window closes, status text turns black
142+
4. **Tap info window** - toast message confirms the click
143+
5. **Tap prev/next buttons** - navigate between markers with camera animation
144+
6. **Tap the FAB** - toggles info window on selected marker
145+
7. **Wait 10 seconds** - info window auto-dismisses, status text turns black
146+
8. **Pan/zoom the map** - markers stay at correct geographic positions
108147

109148
## Marker Locations
110149

111-
This example displays 5 markers:
112-
113-
| Location | Coordinates | Description |
114-
| -------- | ------------------- | ------------------ |
115-
| Center | 51.4661°N, 7.2491°E | Bochum City Center |
116-
| North | 51.4700°N, 7.2550°E | North Location |
117-
| South | 51.4620°N, 7.2430°E | South Location |
118-
| West | 51.4680°N, 7.2380°E | West Location |
119-
| East | 51.4640°N, 7.2600°E | East Location |
150+
This example displays 6 markers at notable Bochum landmarks:
120151

121-
**Note on marker positioning:** While the 4 outer markers are placed on N, S, W, E sides of the central marker (by adjusting latitude/longitude), they do not appear strictly above, below, left, right on the screen. This is due to the Web Mercator projection used by OpenStreetMap, which distorts distances and angles, especially at higher latitudes. The further from the equator, the more pronounced this distortion becomes.
152+
| Location | Coordinates | Description |
153+
| ----------------- | ------------------- | -------------------- |
154+
| Hauptbahnhof | 51.4783°N, 7.2231°E | Main railway station |
155+
| Ruhr University | 51.4452°N, 7.2622°E | Ruhr-Universitat |
156+
| Rathaus | 51.4816°N, 7.2166°E | City Hall |
157+
| Bermuda3eck | 51.4807°N, 7.2222°E | Entertainment dist. |
158+
| Bergbau-Museum | 51.4892°N, 7.2174°E | Mining Museum |
159+
| Starlight Express | 51.4649°N, 7.2043°E | Musical theater |
122160

123161
## Custom Marker Icons
124162

125-
To use custom marker icons instead of the default red teardrop:
163+
To use custom marker icons instead of the default teardrop:
126164

127165
```kotlin
128166
// Create custom bitmap (e.g., from resources)
@@ -171,6 +209,6 @@ Click detection uses:
171209

172210
## Map Location
173211

174-
**Default Center:** Bochum, Germany (51.4661°N, 7.2491°E) at zoom 14.0
212+
**Default Center:** Calculated from marker positions (~51.47°N, 7.22°E) at zoom 13.0
175213

176-
All 5 markers are positioned around Bochum within ~1km radius.
214+
All 6 markers are positioned around Bochum at real landmark locations.
6.97 MB
Loading
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2025 Alexander Farber
3+
* SPDX-License-Identifier: MIT
4+
*
5+
* This file is part of the OpenMapView project (https://github.com/afarber/OpenMapView)
6+
*/
7+
8+
package de.afarber.openmapview.example03markers
9+
10+
import androidx.compose.ui.graphics.Color
11+
import androidx.compose.ui.unit.dp
12+
import kotlin.time.Duration
13+
import kotlin.time.Duration.Companion.seconds
14+
15+
/**
16+
* Constants for the Example03Markers app: colors, dimensions, and durations.
17+
*/
18+
19+
/** Green color used by OpenStreetMap for parks and forests. */
20+
val OsmParkGreen = Color(0xFFAAD3A2)
21+
22+
/** Pink color used by OpenStreetMap for highways and major roads. */
23+
val OsmHighwayPink = Color(0xFFE892A2)
24+
25+
/** Blue color used by OpenStreetMap for water areas (lakes, rivers). */
26+
val OsmWaterBlue = Color(0xFFAAD3DF)
27+
28+
/** Shared corner radius for all toolbar components (matches Material3 FAB). */
29+
val ToolbarCornerRadius = 16.dp
30+
31+
/** Duration after which info windows are automatically dismissed. */
32+
val InfoWindowAutoDismissDuration: Duration = 5.seconds

0 commit comments

Comments
 (0)