paint-brush
Mobile Karten mit Jetpack Compose transformieren: Erkenntnisse von Google I/O für Entwicklervon@darrylbayliss
Neue Geschichte

Mobile Karten mit Jetpack Compose transformieren: Erkenntnisse von Google I/O für Entwickler

von Darryl Bayliss22m2024/07/09
Read on Terminal Reader

Zu lang; Lesen

Jetpack Compose für Maps hat die moderne Android-Entwicklung revolutioniert und mobiles Mapping einfacher und effizienter gemacht. Entdecken Sie wichtige Techniken und Tipps von Google I/O Extended 2023 zur Integration von Google Maps in Ihre Jetpack Compose-Projekte.
featured image - Mobile Karten mit Jetpack Compose transformieren: Erkenntnisse von Google I/O für Entwickler
Darryl Bayliss HackerNoon profile picture
0-item



Dieser Beitrag basiert auf einem Vortrag, der im Juli 2023 bei Google I/O Extended für die Google Developers Group in London gehalten wurde. Die Folien zum Vortrag sind hier verfügbar.

Jetpack Compose für Karten

Es ist schwer vorstellbar, dass Jetpack Compose 1.0 im Juli 2021 veröffentlicht wurde. Zwei Jahre später, da 24 % der Top-1000-Apps bei Google Play Compose verwenden, ist es leicht zu verstehen, warum.



Bei all der Aufregung gibt es einen Bereich der modernen Android-Entwicklung, der meiner Meinung nach wenig Beachtung findet: Google Maps. Es ist schon eine Weile her, seit ich das SDK verwendet habe, daher war ich angenehm überrascht, dass Google Maps mit der Zeit Schritt hält und seine eigene Compose-Bibliothek veröffentlicht.


Das sind gute Nachrichten für Unternehmen und Ingenieure, die im Bereich Kartografie arbeiten. Mobile Kartografie ist eine 35,5 Milliarden Dollar schwere Branche, die Prognosen zufolge bis 2028 auf 87,7 Milliarden Dollar anwachsen wird, bei einer durchschnittlichen jährlichen Wachstumsrate (CAGR) von 19,83 %. Quelle


Wachstumsmärkte Graph


Warum ist das wichtig? Ein größerer Markt bedeutet mehr Möglichkeiten für Unternehmen, mit mobilen Kartenanwendungen Umsatz zu erzielen. Diese reichen von den üblichen Anwendungsfällen wie Essen, Lebensmittellieferungen und Fahrdiensten. Wenn man jedoch tiefer gräbt, gibt es Anwendungen, die nicht sofort offensichtlich sind.


Nachfolgend sind die Beispiele aufgeführt, die ich nach einer kurzen Suche finden konnte:


Wachstumsmärkte Mobile Karten eignen sich hervorragend für Smart Cities. Sie helfen dabei, den Herzschlag einer Stadt zu steuern und Daten so zu visualisieren, dass man die Herausforderungen der Stadt besser versteht und darauf reagieren kann. Nützlich für Stadtplaner, Notfallorganisationen oder normale Bürger.


Auch das Ressourcenmanagement profitiert von Kartierungslösungen. Von der Landwirtschaft über die Fischerei und den Bergbau bis hin zur Forstwirtschaft bieten Karten den in diesen Branchen tätigen Personen eine Perspektive, die ihnen hilft, die richtigen Entscheidungen für eine nachhaltige Rohstoffgewinnung zu treffen.


Der Transportbereich ist in hohem Maße auf Kartentechnologie angewiesen. Dabei geht es nicht nur um Verbraucher-Apps wie Google Maps oder Uber, sondern auch um Funktionen auf Unternehmensebene, wie beispielsweise die Standortermittlung des Fuhrparks eines Unternehmens. Transportunternehmen nutzen Karten auch zur Verkehrssteuerung und helfen bei der Entscheidungsfindung, wohin der Verkehr geleitet werden soll, um den Verkehrsfluss zu verbessern.


Angesichts des Klimawandels und der zunehmenden Unberechenbarkeit des Wetters können Wetterdienste, Katastrophenschutzeinheiten und Naturschützer mithilfe von Karten schließlich nachvollziehen, wie sich unsere Welt verändert und welche positiven Schritte wir unternehmen können, um diesen Wandel einzudämmen.

Quellen:Mordor Intelligence , GMInsights , Allied Market Research , EMR Research , Google Earth Outreach , Research & Markets


Da die Welt immer mehr Daten liefert, ist es an der Zeit zu lernen, wie man diese Daten auf einer Karte darstellt. Lassen Sie uns das tun und dann zum Code zurückkehren.


Verwenden von Google Maps zum Verfassen von Texten

Google Maps für Compose basiert auf den folgenden Abhängigkeiten:

 dependencies { implementation "com.google.maps.android:maps-compose:2.11.4" implementation "com.google.android.gms:play-services-maps:18.1.0" // Optional Util Library implementation "com.google.maps.android:maps-compose-utils:2.11.4" implementation 'com.google.maps.android:maps-compose-widgets:2.11.4' // Optional Accompanist permissions to request permissions in compose implementation "com.google.accompanist:accompanist-permissions:0.31.5-beta" }


Google Maps für Compose basiert auf dem Google Maps SDK. Sie müssen daher die Compose-Bibliothek und das Maps SDK importieren. Die meisten Objekte im Google Maps SDK müssen Sie nicht verwenden, da die Compose-Bibliothek die meisten davon in Composables einbindet.


Die Utils- und Widgets-Bibliotheken sind eine optionale Abhängigkeit. Die Utils-Bibliothek bietet die Möglichkeit, Markierungen auf den Karten zu gruppieren, während Widgets zusätzliche UI-Komponenten bereitstellen. Sie werden diese später im Einsatz sehen.


Für diesen Beitrag habe ich die Bibliothek zum Anfordern von Berechtigungen von Accompanist eingefügt, um zu demonstrieren, wie Standortberechtigungen angefordert werden, eine häufig verwendete Berechtigung für Karten. Accompanist ist eine experimentelle Bibliothek für Google, um Funktionen auszuprobieren und Feedback zu sammeln, die noch nicht Teil von Jetpack Compose sind.


Zum Schluss müssen Sie zur Google Developer Console gehen, sich für einen Google Maps SDK API-Schlüssel anmelden und ihn zu Ihrem Projekt hinzufügen. In den Google Maps Developer Docs finden Sie eine Anleitung dazu.


Sicherheitstipp: Sperren Sie Ihren API-Schlüssel in der Google Developer Console, sodass er nur mit Ihrer Anwendung funktioniert. Dadurch wird eine unbefugte Nutzung verhindert.


Anzeigen einer Karte

Das Anzeigen einer Karte ist so einfach wie folgt:


 setContent { val hydePark = LatLng(51.508610, -0.163611) val cameraPositionState = rememberCameraPositionState { position = CameraPosition.fromLatLngZoom(hydePark, 10f) } GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState) { Marker( state = MarkerState(position = hydePark), title = "Hyde Park", snippet = "Marker in Hyde Park" ) } }


Erstellen Sie ein LatLng Objekt mit der Position eines Bereichs und verwenden Sie es in Verbindung mit rememberCameraPositionState , um die Anfangsposition der Kamera festzulegen. Diese Methode merkt sich die Position der Karte, wenn Sie sich mit den Händen oder programmgesteuert bewegen. Ohne diese Methode würde Compose die Karte bei jeder Statusänderung auf ihre Anfangsposition zurückrechnen.


Erstellen Sie als Nächstes eine GoogleMap Komposition und übergeben Sie einen Modifikator Ihrer Wahl sowie den Kamerastatus. GoogleMap bietet außerdem eine Slot-API zum Übergeben zusätzlicher Kompositionen. Diese Kompositionen sind das, was Sie auf der Karte zeichnen möchten.


Fügen Sie einen zusammensetzbaren Marker hinzu und fügen Sie dann einen MarkerState hinzu, der die Position des Markers im Inneren enthält. Fügen Sie abschließend einen Titel und eine Beschreibung des Markers hinzu.


Wenn man dies ausführt, erhält man eine schöne Luftansicht von West-London mit einer Markierung im Hyde Park.


Markierung im Hyde Park

Anpassen des Markierungsfensters

Sie können das Fenster des Markers anpassen, indem Sie ein MarkerInfoWindowContent Composable verwenden. Dies hat auch eine Slot-basierte API, d. h. Sie können Ihre Composables übergeben, um Ihre benutzerdefinierte Benutzeroberfläche im Fenster darzustellen.


 setContent { val hydePark = LatLng(51.508610, -0.163611) val cameraPositionState = rememberCameraPositionState { position = CameraPosition.fromLatLngZoom(hydePark, 10f) } GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState) { MarkerInfoWindowContent( state = MarkerState(position = hydePark), title = "Hyde Park", snippet = "Marker in Hyde Park" ) { marker -> Column(horizontalAlignment = Alignment.CenterHorizontally) { Text( modifier = Modifier.padding(top = 6.dp), text = marker.title ?: "", fontWeight = FontWeight.Bold ) Text("Hyde Park is a Grade I-listed parked in Westminster") Image( modifier = Modifier .padding(top = 6.dp) .border( BorderStroke(3.dp, color = Color.Gray), shape = RectangleShape ), painter = painterResource(id = R.drawable.hyde_park), contentDescription = "A picture of hyde park" ) } } } }


Wenn Sie dies ausführen, wird das benutzerdefinierte Fenster über der Markierung angezeigt, wenn Sie darauf tippen.


Benutzerdefinierter Fenstermarker in Hyde Park

Mehrere Markierungen anzeigen

Das Anzeigen mehrerer Markierungen ist ganz einfach: Fügen Sie so viele hinzu, wie Sie benötigen. Lassen Sie uns Markierungen für ein paar verschiedene Parks in West-London hinzufügen.


 setContent { val hydePark = LatLng(51.508610, -0.163611) val regentsPark = LatLng(51.531143, -0.159893) val primroseHill = LatLng(51.539556, -0.16076088) val cameraPositionState = rememberCameraPositionState { position = CameraPosition.fromLatLngZoom(hydePark, 10f) } GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState) { // Marker 1 Marker( state = MarkerState(position = hydePark), title = "Hyde Park", snippet = "Marker in Hyde Park" ) // Marker 2 Marker( state = MarkerState(position = regentsPark), title = "Regents Park", snippet = "Marker in Regents Park" ) // Marker 3 Marker( state = MarkerState(position = primroseHill), title = "Primrose Hill", snippet = "Marker in Primrose Hill" ) } }


Führen Sie den Code aus und Sie sehen, wie Ihre Markierungen auf der Karte erscheinen.


Mehrere Parkmarkierungen


Clustering-Marker

Eine Karte kann innerhalb kurzer Zeit sehr voll werden. Wenn Sie versuchen, 300 Markierungen anzuzeigen, wird es für einen Benutzer visuell schwierig, zu verstehen, was vor sich geht. Google Maps und Ihr Gerät werden es Ihnen auch nicht danken, da jede einzelne Markierung gerendert werden muss, was sich auf die Leistung und die Akkulaufzeit auswirkt.


Die Lösung hierfür ist Clustering , eine Technik, bei der nahe beieinander liegende Markierungen zu einer einzigen Markierung gruppiert werden. Dieses Clustering erfolgt auf Zoom-Ebene. Wenn Sie die Karte verkleinern, werden die Markierungen zu einem Cluster gruppiert, wenn Sie hineinzoomen, wird der Cluster in einzelne Markierungen aufgeteilt.


Google Maps für Compose stellt dies standardmäßig über ein Clustering Composable bereit. Es ist nicht erforderlich, komplexe Sortier- oder Filterfunktionen zu schreiben, damit das Clustering erfolgen kann.


 setContent { val hydePark = LatLng(51.508610, -0.163611) val regentsPark = LatLng(51.531143, -0.159893) val primroseHill = LatLng(51.539556, -0.16076088) val crystalPalacePark = LatLng(51.42153, -0.05749) val greenwichPark = LatLng(51.476688, 0.000130) val lloydPark = LatLng(51.364188, -0.080703) val cameraPositionState = rememberCameraPositionState { position = CameraPosition.fromLatLngZoom(hydePark, 10f) } GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState) { val parkMarkers = remember { mutableStateListOf( ParkItem(hydepark, "Hyde Park", "Marker in hyde Park"), ParkItem(regentspark, "Regents Park", "Marker in Regents Park"), ParkItem(primroseHill, "Primrose Hill", "Marker in Primrose Hill"), ParkItem(crystalPalacePark, "Crystal Palace", "Marker in Crystal Palace"), ParkItem(greenwichPark, "Greenwich Park", "Marker in Greenwich Park"), ParkItem(lloydPark, "Lloyd park", "Marker in Lloyd Park"), ) } Clustering(items = parkMarkers, onClusterClick = { // Handle when the cluster is tapped }, onClusterItemClick = { marker -> // Handle when a marker in the cluster is tapped }) } } data class ParkItem( val itemPosition: LatLng, val itemTitle: String, val itemSnippet: String) : ClusterItem { override fun getPosition(): LatLng = itemPosition override fun getTitle(): String = itemTitle override fun getSnippet(): String = itemSnippet }


Beachten Sie die hinzugefügte Datenklasse ParkItem . Wir benötigen diese, da Elemente, die an ein Clustering Composable übergeben werden, der ClusterItem Schnittstelle entsprechen müssen. Die Schnittstelle stellt dem Cluster die Position, den Titel und den Snippet für jeden Marker zur Verfügung.

Zoomen Sie hinein und heraus, und Sie sehen die Clusterung in Aktion.


Standortberechtigung erhalten

Karten und Benutzerposition gehen oft Hand in Hand. Daher ist es für einige Karten-Apps sinnvoll, nach der Berechtigung zur Angabe des Standorts des Benutzers zu fragen.

Gehen Sie in diesem Fall respektvoll mit der Berechtigung eines Benutzers um , da die Standortberechtigung eine der sensibelsten Berechtigungen ist, die Sie von einem Benutzer einholen können.


Informieren Sie den Benutzer unbedingt darüber, warum Sie diese Berechtigung benötigen, und zeigen Sie ihm aktiv auf, welche Vorteile die Gewährung dieser Berechtigung mit sich bringt. Bonuspunkte gibt es, wenn Ihre App teilweise ohne Berechtigung funktioniert.


Google stellt einige gute Anleitungen zum Umgang mit dem Standort der Benutzer sowie eine separate Anleitung zum Zugriff auf Standortdaten im Hintergrund bereit.


Sie haben also Ihre Sorgfaltspflicht erfüllt und entschieden, dass Sie die Berechtigung des Benutzers benötigen, um auf den Standort zuzugreifen. Mit der Berechtigungsbibliothek in Accompanist gehen Sie folgendermaßen vor:


 // Don't forget to add the permissions to AndroidManifest.xml val allLocationPermissionState = rememberMultiplePermissionsState( listOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION) ) // Check if we have location permissions if (!allLocationPermissionsState.allPermissionsGranted) { // Show a component to request permission from the user Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, modifier = Modifier .padding(horizontal = 36.dp) .clip(RoundedCornerShape(16.dp)) .background(Color.white) ) { Text( modifier = Modifier.padding(top = 6.dp), textAlign = TextAlign.Center, text = "This app functions 150% times better with percise location enabled" ) Button(modifier = Modifier.padding(top = 12.dp), onClick = { allLocationPermissionsState.launchMultiplePermissionsRequest() }) { Text(text = "Grant Permission") } } }


Über den Begleiter prüfen wir, ob die App Zugriff auf die Berechtigung ACCESS_FINE_LOCATION oder eine hohe GPS-Genauigkeit in Englisch hat. Es ist wichtig, die angeforderten Berechtigungen in das Android-Manifest aufzunehmen, da Sie die Berechtigungen sonst nicht anfordern können.


Das Android-System und der Google Play Store verwenden das Manifest auch, um zu verstehen, wie Ihre App funktioniert, und um Benutzer zu informieren.

Wenn die Berechtigung nicht erteilt wird, wird ein kleines Dialogfenster angezeigt, in dem die Notwendigkeit der Berechtigung erläutert wird. Außerdem wird eine Schaltfläche zum Starten der Berechtigungsanforderung über das System angezeigt.



Animieren der Karte

Während die meisten Karten-Apps erfordern, dass der Benutzer die Karte per Berührung bewegt, bietet Google Maps für Compose APIs, um die Karte programmgesteuert zu bewegen. Dies kann nützlich sein, wenn Sie als Reaktion auf ein Ereignis zu einem bestimmten Bereich navigieren möchten.


In diesem Beispiel navigieren wir die App sanft durch unsere Sammlung von Markierungen.


 Box(contentAlignment = Alignment.Center) { GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState ) { Clustering(items = parkMarkers, onClusterClick = { // Handle when the click is tapped false }, onClusterItemClick = { marker -> // Handle when the marker is tapped }) LaunchedEffect(key1 = "Animation") { for (marker in parkMarkers) { cameraPositionState.animate( CameraUpdateFactory.newLatLngZoom( marker.itemPosition, // LatLng 16.0f), // Zoom level 2000 // Animation duration in millis ), delay(4000L) // Delay in millis } } } }


Der Schlüssel ist hier der Code innerhalb von LaunchedEffect . Für jeden Marker richtet die App einen Aufruf von cameraPositionState.animate() ein, um zum Marker zu navigieren. Die Kamera erhält diese Informationen über ein Update der Kamera, das mit CameraUpdateFactory.newLatLngZoom() erstellt wird.


Diese Methode verwendet einen LatLng , einen Float-Wert, der die Zoomstufe der Karte angibt, und einen Long-Wert, um die Dauer der Animation festzulegen.

Um die Animationen zu verteilen, verwenden wir delay() um zwischen jeder Animation eine 4-sekündige Pause einzufügen.


Street View wird angezeigt

Google Maps for Compose hilft Ihnen nicht nur bei der Erstellung von Luftbildern. Sie können Apps auch Zugriff auf Street View gewähren, um eine 360-Grad-Ansicht eines Standorts anzuzeigen. Dies erreichen Sie mit dem StreetView Composable:


 var selectedMarker: ParkItem? by remember { mutableStateOf(null) } if (selectedMarker != null) { StreetView(Modifier.fillMaxSize(), streetViewPanoramaOptionsFactory = { StreetViewPanoramaOptions().position(selectedMarker!!.position) }) } else { Box(contentAlignment = Alignment.Center) { GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState ) { Clustering(items = parkMarkers, onClusterClick = { // Handle when the cluster is clicked false }, onClusterItemClick = { marker -> // Handle when a marker in the cluster is clicked selectedMarker = marker false }) } } }


In diesem Beispiel setzen wir die Variable selectedMarker , wenn auf einen Marker getippt wird. Wenn ein Marker ausgewählt wird, zeigen wir Street View an und geben die Position des Markers weiter.



Formen zeichnen

Vielleicht möchten Sie Ihre eigenen Formen und Anmerkungen auf der Karte zeichnen. Google Maps for Compose bietet hierfür eine Reihe von Composables. In diesem Beitrag verwenden wir das Composable Circle .


Ein Kreis ist eine gute Form, wenn Ihre App Geofences verwendet, um auf Änderungen des Standorts eines Benutzers zu reagieren. Ein Kreis kann den Bereich darstellen, in dem ein Geofence aktiv ist.


 Box(contentAlignment = Alignment.Center) { GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState ) { Clustering(items = parkMarkers, onClusterClick = { // Handle when the cluster is clicked false }, onClusterItemClick = { marker -> // Handle when a marker in the cluster is clicked selectedMarker = marker false }) } } parkMarkers.forEach { Circle( center = it.position, radius = 120.0, fillColor = Color.Green, strokeColor = Color.Green ) }


Hier richten wir für jeden unserer Marker einen Kreis ein. Um einen Kreis zu erstellen, müssen wir ihm eine Position und die Größe des Radius für den Kreis übergeben. Wir verwenden auch zwei optionale Parameter, um die Farbe des Rahmens festzulegen und die Farbe für den Kreis zu füllen.



Anzeigen einer Maßstabsleiste

Zu einer guten Karte gehören oft Legenden und Diagramme, die zeigen, wie viel Entfernung ein Raummaß auf der Karte bedeutet. So erhalten Sie eine Vorstellung von den auf der Karte dargestellten Räumen, da nicht jede Karte dieselbe Maßeinheit verwendet.


Bei digitalen Karten, die vergrößert und verkleinert werden können, wird dies noch komplexer, da sich die dargestellten Entfernungen dynamisch ändern können. Glücklicherweise bietet Google Maps für Compose dafür eine Lösung.


Mithilfe der Widgets-Bibliothek erhalten Sie Zugriff auf die Composables DisappearingScaleBar und ScaleBar . Dabei handelt es sich um UI-Komponenten, die sich oben auf der Karte befinden und Benutzern eine Entfernungsangabe bereitstellen, die sich je nach Zoomstufe ändert.


 Box(contentAlignment = Alignment.Center) { GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState ) { // You can also use ScaleBar DisappearingScaleBar( modifier = Modifier .padding(top = 5.dp, end = 15.dp) .align(Alignment.TopStart), cameraPositionState = cameraPositionState ) Clustering(items = parkMarkers, onClusterClick = { // Handle when the cluster is clicked false }, onClusterItemClick = { marker -> // Handle when a marker in the cluster is clicked selectedMarker = marker false }) } } parkMarkers.forEach { Circle( center = it.position, radius = 120.0, fillColor = Color.Green, strokeColor = Color.Green ) }


Nach dem Hinzufügen des Composable erhalten Sie eine ScaleBar, die sich je nach Zoomstufe oben auf der Karte ändert.




Hilfe und Unterstützung

Google Maps for Compose ist eine großartige Möglichkeit, mit Google Maps zu arbeiten, und es gibt noch viel mehr zu lernen. Hier sind einige Orte, die ich empfehle, wenn Sie Hilfe benötigen:

  • Google Maps für Compose Repo : Das Repo mit dem Quellcode der Bibliothek. Enthält Codebeispiele zur Verwendung der Bibliothek und wo Sie Ihre Fehlerberichte und Beiträge einreichen können.
  • Website zu Google Maps für Android : Hier erfahren Sie alles über die Konzepte von Google Maps für Android. Diese sind zwar sehr allgemein gehalten und verwenden nicht die Compose-Bibliothek, sind aber dennoch wichtig zu kennen, da sie im Hintergrund verwendet werden.
  • Google Maps Platform Discord Der offizielle Discord-Server für Google Maps. Hier finden Sie Leute, die über Google Maps für mehrere Plattformen diskutieren, um Hilfe bitten und diese anbieten und ihre eigene Arbeit präsentieren.