Skip to content

Commit a974a39

Browse files
committed
Add Example06Clicks
1 parent 3131c37 commit a974a39

12 files changed

Lines changed: 330 additions & 0 deletions

File tree

.github/workflows/_build-examples.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ jobs:
4949
- name: Build Example05Camera
5050
run: ./gradlew :examples:Example05Camera:assembleDebug
5151

52+
- name: Build Example06Clicks
53+
run: ./gradlew :examples:Example06Clicks:assembleDebug
54+
5255
- name: Upload example APKs
5356
uses: actions/upload-artifact@v4
5457
with:

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ Shows how to draw vector shapes including polylines, filled polygons, and polygo
6969

7070
Demonstrates smooth camera animations with customizable durations and completion callbacks.
7171

72+
### [Example06Clicks](examples/Example06Clicks) - Map Click Listeners
73+
74+
Demonstrates map click and long-click listeners with coordinate display via Toast messages.
75+
7276
## Documentation
7377

7478
- [Public API Compatibility](docs/PUBLIC_API.md) - Google MapView API implementation status and compatibility matrix
-4.48 MB
Loading
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
plugins {
2+
id("com.android.application")
3+
id("org.jetbrains.kotlin.android")
4+
id("org.jetbrains.kotlin.plugin.compose")
5+
id("com.diffplug.spotless")
6+
}
7+
8+
android {
9+
namespace = "de.afarber.openmapview.example06clicks"
10+
compileSdk = 35
11+
12+
defaultConfig {
13+
applicationId = "de.afarber.openmapview.example06clicks"
14+
minSdk = 23
15+
targetSdk = 35
16+
versionCode = 1
17+
versionName = "1.0"
18+
}
19+
20+
buildTypes {
21+
release {
22+
isMinifyEnabled = false
23+
}
24+
}
25+
26+
compileOptions {
27+
sourceCompatibility = JavaVersion.VERSION_17
28+
targetCompatibility = JavaVersion.VERSION_17
29+
}
30+
31+
kotlinOptions {
32+
jvmTarget = "17"
33+
}
34+
35+
buildFeatures {
36+
compose = true
37+
}
38+
39+
composeOptions {
40+
kotlinCompilerExtensionVersion = "1.5.3"
41+
}
42+
43+
sourceSets {
44+
getByName("main").java.srcDirs("src/main/kotlin")
45+
}
46+
}
47+
48+
dependencies {
49+
implementation(project(":openmapview"))
50+
implementation("androidx.core:core-ktx:1.15.0")
51+
implementation("androidx.activity:activity-compose:1.9.3")
52+
implementation("androidx.compose.ui:ui:1.7.5")
53+
implementation("androidx.compose.material3:material3:1.3.1")
54+
implementation("androidx.compose.ui:ui-viewbinding:1.7.5")
55+
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.7")
56+
}
57+
58+
spotless {
59+
kotlin {
60+
target("src/**/*.kt")
61+
ktlint("1.3.1").editorConfigOverride(
62+
mapOf(
63+
"ktlint_function_naming_ignore_when_annotated_with" to "Composable",
64+
),
65+
)
66+
trimTrailingWhitespace()
67+
endWithNewline()
68+
indentWithSpaces(4)
69+
licenseHeaderFile(rootProject.file("spotless.license.kt"), "(package|import)")
70+
}
71+
kotlinGradle {
72+
target("*.kts")
73+
ktlint("1.3.1")
74+
}
75+
}
5.04 MB
Loading
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
3+
<uses-permission android:name="android.permission.INTERNET" />
4+
5+
<application
6+
android:allowBackup="true"
7+
android:icon="@android:drawable/ic_dialog_map"
8+
android:label="Example06Clicks"
9+
android:supportsRtl="true"
10+
android:theme="@android:style/Theme.Material.Light.NoActionBar">
11+
<activity
12+
android:name=".MainActivity"
13+
android:exported="true">
14+
<intent-filter>
15+
<action android:name="android.intent.action.MAIN" />
16+
<category android:name="android.intent.category.LAUNCHER" />
17+
</intent-filter>
18+
</activity>
19+
</application>
20+
</manifest>
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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.example06clicks
9+
10+
import android.content.Intent
11+
import android.net.Uri
12+
import android.os.Bundle
13+
import android.widget.Toast
14+
import androidx.activity.ComponentActivity
15+
import androidx.activity.compose.setContent
16+
import androidx.compose.foundation.layout.fillMaxSize
17+
import androidx.compose.material3.MaterialTheme
18+
import androidx.compose.material3.Surface
19+
import androidx.compose.runtime.Composable
20+
import androidx.compose.runtime.LaunchedEffect
21+
import androidx.compose.ui.Modifier
22+
import androidx.compose.ui.platform.LocalContext
23+
import androidx.compose.ui.viewinterop.AndroidView
24+
import de.afarber.openmapview.LatLng
25+
import de.afarber.openmapview.OnMapClickListener
26+
import de.afarber.openmapview.OnMapLongClickListener
27+
import de.afarber.openmapview.OpenMapView
28+
29+
class MainActivity : ComponentActivity() {
30+
override fun onCreate(savedInstanceState: Bundle?) {
31+
super.onCreate(savedInstanceState)
32+
setContent {
33+
MaterialTheme {
34+
Surface(
35+
modifier = Modifier.fillMaxSize(),
36+
color = MaterialTheme.colorScheme.background,
37+
) {
38+
MapViewScreen()
39+
}
40+
}
41+
}
42+
}
43+
}
44+
45+
@Composable
46+
fun MapViewScreen() {
47+
val context = LocalContext.current
48+
val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current
49+
50+
// Show initial instruction toast
51+
LaunchedEffect(Unit) {
52+
Toast.makeText(
53+
context,
54+
"Try dragging the map around.\nTry short and long clicks on the map",
55+
Toast.LENGTH_LONG,
56+
).show()
57+
}
58+
59+
AndroidView(
60+
factory = { ctx ->
61+
OpenMapView(ctx).apply {
62+
// Register lifecycle observer for proper cleanup
63+
lifecycleOwner.lifecycle.addObserver(this)
64+
65+
setCenter(LatLng(51.4661, 7.2491)) // Bochum, Germany
66+
setZoom(14.0)
67+
68+
// Set map click listener
69+
setOnMapClickListener(
70+
OnMapClickListener { latLng ->
71+
val coordString = "%.4f, %.4f".format(latLng.latitude, latLng.longitude)
72+
Toast.makeText(
73+
context,
74+
"Clicked at: $coordString",
75+
Toast.LENGTH_SHORT,
76+
).show()
77+
},
78+
)
79+
80+
// Set map long-click listener
81+
setOnMapLongClickListener(
82+
OnMapLongClickListener { latLng ->
83+
val coordString = "%.4f, %.4f".format(latLng.latitude, latLng.longitude)
84+
Toast.makeText(
85+
context,
86+
"Long-clicked at: $coordString",
87+
Toast.LENGTH_SHORT,
88+
).show()
89+
},
90+
)
91+
92+
// Set attribution click listener to open OSM copyright page
93+
setOnAttributionClickListener {
94+
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://www.openstreetmap.org/copyright"))
95+
context.startActivity(intent)
96+
}
97+
}
98+
},
99+
modifier = Modifier.fillMaxSize(),
100+
)
101+
}

openmapview/src/main/kotlin/de/afarber/openmapview/MapController.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,31 @@ class MapController(
203203
zoom = zoom,
204204
)
205205

206+
/**
207+
* Converts screen coordinates to geographic coordinates.
208+
*
209+
* Takes into account the current camera position, zoom level, and pan offset.
210+
*
211+
* @param screenX The X coordinate in screen pixels
212+
* @param screenY The Y coordinate in screen pixels
213+
* @return The geographic location (LatLng) at that screen position
214+
*/
215+
fun screenToLatLng(
216+
screenX: Float,
217+
screenY: Float,
218+
): LatLng {
219+
// Get center pixel coordinates at current zoom
220+
val (centerPixelX, centerPixelY) = Projection.latLngToPixel(center, zoom.toInt())
221+
222+
// Convert screen coordinates to pixel coordinates
223+
// Account for view center offset and pan offset
224+
val pixelX = (centerPixelX + (screenX - viewWidth / 2 + panOffsetX).toDouble()).toInt()
225+
val pixelY = (centerPixelY + (screenY - viewHeight / 2 + panOffsetY).toDouble()).toInt()
226+
227+
// Convert pixel coordinates to LatLng
228+
return Projection.pixelToLatLng(pixelX, pixelY, zoom.toInt())
229+
}
230+
206231
/**
207232
* Moves the camera instantly to a new position.
208233
*
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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
9+
10+
/**
11+
* Listener interface for map click events.
12+
*
13+
* Called when the user taps on the map (not on a marker or other overlay).
14+
*/
15+
fun interface OnMapClickListener {
16+
/**
17+
* Called when the map is clicked.
18+
*
19+
* @param latLng The geographic location that was clicked
20+
*/
21+
fun onMapClick(latLng: LatLng)
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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
9+
10+
/**
11+
* Listener interface for map long-click events.
12+
*
13+
* Called when the user long-presses on the map (not on a marker or other overlay).
14+
*/
15+
fun interface OnMapLongClickListener {
16+
/**
17+
* Called when the map is long-clicked.
18+
*
19+
* @param latLng The geographic location that was long-clicked
20+
*/
21+
fun onMapLongClick(latLng: LatLng)
22+
}

0 commit comments

Comments
 (0)