meta data for this page
Google Map
Documentation: https://developers.google.com/maps/documentation/android-sdk/map#view_the_code
Pour pouvoir utiliser une carte Google Map, il faut créer une clé d'API et l'insérer dans le fichier AndroidManifest.xml à la place de YOUR_API_KEY
Se rendre sur la console Google pour définir la clé.
Suivre les indications sur cette page : Créer des clés API
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ca.cegepgarneau.google_map"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Google_Map"> <!-- TODO: Before you run your application, you need a Google Maps API key. To get one, follow the directions here: https://developers.google.com/maps/documentation/android-sdk/get-api-key Once you have your API key (it starts with "AIza"), define a new property in your project's local.properties file (e.g. MAPS_API_KEY=Aiza...), and replace the "YOUR_API_KEY" string in this file with "${MAPS_API_KEY}". --> <meta-data android:name="com.google.android.geo.API_KEY" android:value="YOUR_API_KEY" /> <activity android:name=".MapsActivity" android:label="@string/title_activity_maps"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Restriction de la clé
Dans la console Google, sélectionner Application Android
(1)
Ajouter ensuite une restriction (3) en saisissant le nom du package de votre application, et l'empreinte numérique SHA-1
Pour trouver l'empreinte numérique SHA-1 de votre application, suivre les indications (2) suivant votre système d'exploitation.
Ajout de la carte dans une activité
Voir démo et documentation
https://developers.google.com/maps/documentation/android-sdk/map
Fenêtre d'information
Implémentation des interfaces GoogleMap.OnInfoWindowClickListener
, GoogleMap.InfoWindowAdapter
(voir démo)
// détection du click sur une fenêtre d'information d'un marqueur mMap.setOnInfoWindowClickListener(this); // Permet de modifier l'apparence de la fenêtre d'information d'un marqueur mMap.setInfoWindowAdapter(this);
Afficher sa position
Documentation: https://developers.google.com/maps/documentation/android-sdk/location
Demande de permission ACCESS_COARSE_LOCATION
ou ACCESS_FINE_LOCATION
La méthode setMyLocationEnabled(true)
permet d'afficher le bouton pour centrer la carte sur la position de l'utilisateur.
La méthode setOnMyLocationButtonClickListener(this)
avec onMyLocationButtonClick()
permet de détecter le click sur ce bouton (1)
La méthode setOnMyLocationClickListener(this)
avec onMyLocationClick()
permet de détecter le click sur la position de l'utilisateur (2)
Suivre la localisation
Documentation:
Ajout de play-services-location dans le build.gradle du module
// Google Play Service implementation 'com.google.android.gms:play-services-location:21.0.1'
Dans MainActivity; création d'une instance du Fused Location Provider
private lateinit var fusedLocationClient: FusedLocationProviderClient // .. override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) }
Retrouver la dernière position connue
fusedLocationClient.lastLocation .addOnSuccessListener(this) { location -> // Vérifie que la position n'est pas null if (location != null) { Log.d(TAG, "onSuccess: $location") // Centre la carte sur la position de l'utilisateur au démarrage val latLng = LatLng(location.latitude, location.longitude) mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 11f)) } }
Il faut penser à ajouter la vérification des permissions (voir démo)
Mise à jour position automatique
Pour avoir une mise à jour régulière de la position ⇒ documentation: https://developer.android.com/training/location/change-location-settings
Configuration pour mise à jour automatique de la position
// Configuration pour mise à jour automatique de la position locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 5000L) .setWaitForAccurateLocation(false) .setMinUpdateIntervalMillis(2000L) .setMaxUpdateDelayMillis(5000L) .build() // Création de la requête pour la mise à jour de la position // avec la configuration précédente val request = LocationSettingsRequest.Builder() .addLocationRequest(locationRequest) .build() // Création du client pour la mise à jour de la position. // Le client va permettre de vérifier si la configuration est correcte, // si l'utilisateur a activé ou désactivé la localisation val client = LocationServices.getSettingsClient(this) // Vérifie que la configuration de la mise à jour de la position est correcte // Si l'utilisateur a activé ou désactivé la localisation client.checkLocationSettings(request) .addOnSuccessListener { Log.d(TAG, "onSuccess: $it") // Si la configuration est correcte, on lance la mise à jour de la position fusedLocationClient.requestLocationUpdates( // Configuration de la mise à jour de la position locationRequest, // Callback pour la mise à jour de la position locationCallback, null ) } .addOnFailureListener { Log.d(TAG, "onFailure: $it") // Si la configuration n'est pas correcte, on affiche un message Toast.makeText(this, "Veuillez activer la localisation", Toast.LENGTH_SHORT).show() }
Définir le callback qui est appelé quand la position change
class MapsActivity : AppCompatActivity(), ...{ // ... // Déclaration pour le callback de la mise à jour de la position de l'utilisateur // Le callback est appelé à chaque fois que la position de l'utilisateur change private var locationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult) { super.onLocationResult(locationResult) userLocation = locationResult.lastLocation Log.d(TAG, "onLocationResult: ${userLocation?.latitude} ${userLocation?.longitude}") } } }
Calculer une distance
Méthode distanceTo()
entre deux objets de type Location
val distance = userLocation?.distanceTo(location)
Détecter un clic sur la carte
mMap.setOnMapClickListener { latLng -> Log.d(TAG, "onMapClick: $latLng") // Ajoute un marqueur à l'endroit cliqué mMap.addMarker( MarkerOptions().position(latLng) .title("Je suis ici !") .draggable(true) ) }
Marqueur déplaçable
mMap.addMarker( MarkerOptions().position(latLng) .title("Je suis ici !") .draggable(true) )
Écouteur pour le drag&drop d'un marqueur
mMap.setOnMarkerDragListener(object : OnMarkerDragListener { override fun onMarkerDragStart(marker: Marker) { Log.d(TAG, "onMarkerDragStart: " + marker.position) } override fun onMarkerDrag(marker: Marker) { Log.d(TAG, "onMarkerDrag: " + marker.position) } override fun onMarkerDragEnd(marker: Marker) { Log.d(TAG, "onMarkerDragEnd: " + marker.position) } })
Connaître la position de la caméra (centre de la carte)
CameraPosition cameraPosition = mMap.getCameraPosition(); LatLng position = new LatLng(cameraPosition.target.latitude, cameraPosition.target.longitude);
Ajouter des informations dans un marqueur
On peut ajouter un titre au marqueur avec la méthode title()
Pour ajouter des informations supplémentaires, il faut utiliser la propriété tag
.
message = new Message(1, "Gaël", "Un message", 46.7819, -71.3571) ... val marker = mMap.addMarker( MarkerOptions() .position(position) .title(message.author) ) if (marker != null) { marker.tag = message }
Grâce à marker.tag
, on pourra retrouver cet objet plus tard dans le code
val message: Message? = marker.tag as Message?
Afficher une image dans la fenêtre d'information
Quand on veut afficher une image qui est sur un serveur dans la fenêtre d'information, elle ne s'affiche pas car le temps qu'elle soit chargée, la fenêtre a déjà fini de se créer. Donc l'image n'apparaît pas.
Une astuce consiste à fermer la fenêtre d'information quand l'image a été chargée puis de ré-ouvrir la fenêtre pour qu'elle se redessine avec l'image avec les méthodes hideInfoWindow()
et showInfoWindow()
.
Voici un exemple d'utilisation avec le librairie Coil-kt et onSuccess
qui permet de mettre en place un écouteur pour savoir quand l'image est bien chargée.
override fun getInfoContents(marker: Marker): View { // Crée une vue à partir du layout personnalisé pour les fenêtres d'information // ... // Initialise les composants de la vue avec leurs identifiants respectifs // ... // Récupère les informations de la propriété associées au marqueur val markerInfo: Property? = marker.tag as Property? if (markerInfo != null) { // Affiche les informations de la propriété dans les composants de la vue tvPrice.text = markerInfo.priceDisplay tvType.text = markerInfo.type tvStreet.text = markerInfo.street tvCity.text = markerInfo.city // Utilise Coil pour charger l'image de la propriété depuis l'URL. val request = ImageRequest.Builder(requireContext()) .data(markerInfo.photos?._600) // URL de l'image .allowHardware(false) // Évite des problèmes de rendu logiciel .target( onSuccess = { result -> // Met à jour l'image de la vue lorsque le chargement réussit ivPhoto.setImageDrawable(result) // Rafraîchit la fenêtre d'information si elle est déjà affichée pour montrer l'image chargée if (marker.isInfoWindowShown) { marker.hideInfoWindow() marker.showInfoWindow() } }, onError = { error -> // Utilise une image par défaut en cas d'erreur de chargement ivPhoto.setImageResource(R.drawable.placeholder) } ) .build() // Enfile la requête de chargement d'image dans le gestionnaire d'images de Coil requireContext().imageLoader.enqueue(request) } // Retourne la vue personnalisée pour être utilisée comme contenu de la fenêtre d'information return view }