Ludovic ROLAND

Blog technique sur mes expériences de développeur.

Intégrer et utiliser une carte google dans une application android (7/7) : Les markers et les infos windows

27 août 2016

Maintenant que vous êtes un peu plus à l’aise avec la personnalisation et les manipulations basiques d’une carte Google, je vous propose de conclure la première partie de ce tutoriel avec les markers et les infos windows.

Pour rappel, un marker est tout simplement une punaise permettant d’identifier un point précis sur une carte comme par exemple la position d’un utilisateur ou d’un POI comme un commerce ou un restaurant. Derrière le mot info window se cache en réalité l’info bulle qui, sur la plupart des cartes, s’ouvre lorsque l’on clique sur un marker, permettant alors d’afficher des informations sur le POI comme par exemple le nom et l’adresse d’un commerce.

Plan

Premiers pas avec les markers

Nous allons donc commencer par voir tout ce qu’il faut savoir sur les markers. Nous allons voir comment créer un marker, le placer sur la carte, le personnaliser, le déplacer et détecter les intéractions avec nos utilisateurs.

Créer un marker et l’ajouter sur la carte

Pour créer un marker, nous allons utiliser la classe MakerOptions. Une fois une instance de la classe créée, nous allons pouvoir utiliser les nombreuses méthodes proposées afin de définir les différentes propriétés du marker. Par exemple, pour définir la position du marker, il convient d’utiliser la méthode position. Cette méthode accepte un paramètre de type LatLng, classe que nous avons déjà manipulée dans le chapitre précédent.

Par exemple, pour créer un marker sur Paris, voici le code à écrire :

@Override
public void onMapReady(GoogleMap googleMap)
{
  final MarkerOptions markerParis = new MarkerOptions();
  markerParis.position(new LatLng(48.8534100, 2.3488000));
}

A noter qu’il est possible tout faire sur une ligne :

@Override
public void onMapReady(GoogleMap googleMap)
{
  final MarkerOptions markerParis = new MarkerOptions().position(new LatLng(48.8534100, 2.3488000));
}

Une fois le marker créé, l’ajouter sur la carte est un véritable jeu d’enfant ! Il suffit de passer notre instance de MarkerOptions en tant que paramètre à la méthode addMarker de la classe GoogleMap. Par exemple :

@Override
public void onMapReady(GoogleMap googleMap)
{
  final MarkerOptions markerParis = new MarkerOptions().position(new LatLng(48.8534100, 2.3488000));
  googleMap.addMarker(markerParis);
}

Si vous exécutez ces deux lignes de code, vous verrez normalement apparaître le marker sur la carte comme en témoigne la capture d’écran ci-dessous :

Bien évidemment, nous aurions pu optimiser légèrement notre code en ne créant pas de variable intermédiaire :

@Override
public void onMapReady(GoogleMap googleMap)
{
  googleMap.addMarker(new MarkerOptions().position(new LatLng(48.8534100, 2.3488000)));
}

Vous êtes maintenant libres d’ajouter d’autres markers sur la carte. Pour cela, il suffit de rappeler la méthode addMarker de notre instance de GoogleMap et de lui passer à chaque fois une nouvelle instance d’un MarkerOptions.

Par exemple, le code suivant permet d’ajouter cinq markers sur la carte :

@Override
public void onMapReady(GoogleMap googleMap)
{
  googleMap.addMarker(new MarkerOptions().position(new LatLng(48.8534100, 2.3488000)));
  googleMap.addMarker(new MarkerOptions().position(new LatLng(10, 10)));
  googleMap.addMarker(new MarkerOptions().position(new LatLng(20, 20)));
  googleMap.addMarker(new MarkerOptions().position(new LatLng(30, 30)));
  googleMap.addMarker(new MarkerOptions().position(new LatLng(40, 40)));
}

Si vous lancez l’application, vous devriez bien y voir apparaître les cinq markers :

Ajouter un titre

Avant de nous attaquer à la personnalisation des markers, nous allons voir qu’il est possible de leur ajouter un titre. Il s’agit d’une opération extrêment simple puisqu’il suffit d’utiliser la méthode title de la classe MarkerOptions. Cette méthode accepte un seul paramètre qui doit être une chaîne de caractères. Cette chaîne de caractères s’affichera alors dans un info window, c’est-à-dire une info bulle au dessus du marker lorsque celui-ci sera sélectionné par l’utilisateur.

Par exemple, voici le code d’un marker placé sur Paris, possédant un titre :

@Override
public void onMapReady(GoogleMap googleMap)
{
  googleMap.addMarker(new MarkerOptions().position(new LatLng(48.8534100, 2.3488000)).title("Paris"));
}

Après avoir cliqué sur le marker, vous devriez voir apparaître l’info window comme en témoigne la capture d’écran suivante :

A noter que si vous avez plusieurs markers seul l’info window du marker actuellement sélectionné par l’utilisateur va s’afficher. Si le marker sélectionné change, les infos windows des autres markers seront automatiquement cachés et celui du marker sélectionné s’affichera automatiquement sans action particulière de notre côté.

Par exemple, le code suivant positionne trois markers avec des titres :

@Override
public void onMapReady(GoogleMap googleMap)
{
  googleMap.addMarker(new MarkerOptions().position(new LatLng(48.8534100, 2.3488000)).title("Paris"));
  googleMap.addMarker(new MarkerOptions().position(new LatLng(10, 10)).title("Nigeria"));
  googleMap.addMarker(new MarkerOptions().position(new LatLng(40, 40)).title("Turquie"));
}

Une fois l’application lancée, vous pouvez vous amuser à cliquer sur les différents markers. Vous verrez au plus, un seul info bulle à l’écran :

Ajouter un sous-titre

Nous venons de voir qu’il était possible d’ajouter un titre à un marker afin qu’il soit affiché dans une info bulle lorsque celui-ci est sélectionné. Sachez qu’il est possible d’aller un tout petit peu plus loin et d’afficher, en dessous du titre, un sous titre. Pour cela, Il convient d’utiliser la méthode snippet de la classe MarkerOptions.

Par exemple, voici le code d’un marker placé sur Paris, possédant un titre et un sous-titre :

@Override
public void onMapReady(GoogleMap googleMap)
{
  googleMap.addMarker(new MarkerOptions().position(new LatLng(48.8534100, 2.3488000)).title("Paris").snippet("is beautiful !"));
}

Après avoir cliqué sur le marker, vous devriez voir apparaître l’info window comme en témoigne la capture d’écran suivante :

Personnaliser les markers

Nous venons de voir comment afficher un ou plusieurs markers sur une carte et comment leur ajouter un titre et un sous-titre. Vous l’aurez cependant remarqué, le marker et sa couleur ne sont pas forcément raccord avec le thème de l’application. C’est pourquoi, je vous propose de voir quelques méthodes de la classe MarkerOptions qui vous nous permettre de personnaliser les markers à afficher sur la carte.

Personnaliser la transparence d’un marker

Comme vous avez pu le constater jusqu’à maintenant, les markers sont opaques. Nous allons voir qu’il est très facile de modifier cette opacité grâce à la méthode alpha de la classe MarkerOptions. Cette méthode accepte un nombre décimal compris entre 0 et 1. Plus le nombre le nombre sera grand et plus le marker sera opaque, 1 étant la valeur par défaut.

Par exemple :

@Override
public void onMapReady(GoogleMap googleMap)
{
  googleMap.addMarker(new MarkerOptions().position(new LatLng(48.8534100, 2.3488000)).alpha(0.3f));
}

Le résultat est le suivant :

Personnaliser la couleur d’un marker

Je vous propose de continuer notre exploration de la personnalisation des markers en nous attaquant à leur couleur. Comme vous avez pu le comprendre dans la première partie de ce chapitre, les markers, par défaut, sont d’une couleur à la croisée du rose et du rouge.

Pour pouvoir changer la couleur d’un marker, nous allons utiliser la méthode icon de la classe MarkerOptions. Cette méthode accepte pour paramètre un objet de la classe BitmapDescriptor. Pour produire un tel objet, nous allons nous aider de la classe BitmapDescriptorFactory et plus précisément de la méthode statique defaultMarker. Cette méthode va en effet nous permettre de produire un marker dont le design est celui par défaut mais dont il est possible de changer la couleur. Bien évidemment, il n’est pas possible d’entièrement personnaliser la couleur du marker via cette méthode. Il convient de la choisir parmi une liste pré-définie. Cette liste est composée de l’ensemble des constantes commençant par HUE_ de la classe BitmapDescriptorFactory.

Par exemple le code suivant permet d’afficher trois markers :

  • un vert
  • un bleu
  • un jaune
@Override
public void onMapReady(GoogleMap googleMap)
{
  googleMap.addMarker(new MarkerOptions().position(new LatLng(48.8534100, 2.3488000)).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)));
  googleMap.addMarker(new MarkerOptions().position(new LatLng(10, 10)).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE)));
  googleMap.addMarker(new MarkerOptions().position(new LatLng(20, 20)).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_YELLOW)));
}

Si vous lancez l’application, voici ce que vous devriez avoir à l’écran :

Personnaliser le design d’un marker

Nous venons donc de voir qu’il est possible de changer la couleur d’un marker. Je vous propose d’aller encore un peu loin en voyant comment il est possible de modifier complètement le design d’un marker en utilisant une image personnalisée.

Comme pour la personnalisation de la couleur du marker, nous allons utiliser la méthode icon de la classe MarkerOptions. Cette méthode accepte toujours un seul paramètre de type BitmapDescriptor. Une nouvelle fois, nous allons utiliser les méthodes statiques de la classe BitmapDescriptorFactory pour produire un tel objet. Dans notre cas, nous allons pouvoir utiliser l’une des cinq méthodes statiques suivantes :

  • La méthode fromAsset qui accepte pour paramètre une chaîne de caractères et qui permet de prendre une image qui se trouve dans le dossier assets de la solution.
  • La méthode fromBitmap qui accepte pour paramètre un objet de type Bitmap vous permettant alors d’utiliser une image que vous auriez par exemple téléchargée depuis internet.
  • La méthode fromFile qui accepte pour paramètre une chaîne de caractères permettant alors d’utiliser une image qui se trouve dans la mémoire interne et au chemin spécifié en paramètre.
  • La méthode fromPath qui accepte pour paramètre une chaîne de caractères permettant d’utiliser une image qui se trouve au chemin absolue spécifié en paramètre.
  • La méthode fromResource qui accepte pour paramètre l’identifiant d’une ressource de la solution.

Dans le cadre de ce tutoriel, je vous propose d’utiliser l’icône par défaut de l’application comme marker. Nous allons donc utiliser la méthode statique fromResource :

@Override
public void onMapReady(GoogleMap googleMap)
{
  googleMap.addMarker(new MarkerOptions().position(new LatLng(48.8534100, 2.3488000)).icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher)));
}

Après l’éxécution de l’application, vous devriez observer le marker suivant sur votre carte :

Personnaliser l’orientation d’un marker

Pour terminer sur la personnalisation des markers, je vous propose de voir comment il est possible de changer l’orientation d’un marker sur une carte. Cette personnalisation est d’une extrême simplicité puisqu’il convient d’utiliser la méthode rotation de la classe MarkerOptions. Cette méthode accepte pour paramètre un nombre décimal correspondant à la nouvelle orientation en dégrés.

Par exemple, le code suivant permet de faire tourner le marker de 45° :

@Override
public void onMapReady(GoogleMap googleMap)
{
  googleMap.addMarker(new MarkerOptions().position(new LatLng(48.8534100, 2.3488000)).rotation(45.0f));
}

Si vous lancez l’application, voici ce que vous devriez observer :

Intéragir avec les markers

Nous venons de voir qu’il est possible de personnaliser les markers positionnés sur une carte. Je vous propose de continuer à travailler avec les markers en voyant maintenant comment il est possible d’intéragir avec. Je vous propose d’étudier deux intéractions : le clic et le déplacement.

Cliquer sur un marker

Avec les connaissances que nous avons jusque là, nous ne sommes pas en mesure de détecter lorsqu’un utilisateur clique sur l’un des markers positionnés sur la carte. Je vous propose de corriger ça tout de suite.

Pour être en mesure de détecter le clic de l’utilisateur sur un des markers de la carte, nous allons devoir utiliser et implémenter l’interface OnMarkerClickListener. Cette interface doit ensuite être renseignée auprès de notre instance de GoogleMap grâce à la méthode setOnMarkerClickListener. L’implémentation de cette interface, nous oblige à implémenter la méthode onMarkerClick suivante :

@Override
public boolean onMarkerClick(Marker marker)
{
  return false;
}

Comme vous pouvez le constater, cette méthode nous fournit, via son unique paramètre, le marker sur lequel l’utilisateur vient de cliquer. Cette méthode retourne un booléen :

  • true si l’événement de clic a bien été consommé dans notre méthode
  • false si l’événement de clic n’a pas été consommé dans notre méthode et que le système doit alors utiliser le comportement par défaut, à savoir centrer le marker et afficher si possible l’info window.

A partir du marker fourni par la méthode onMarkerClick, vous pouvez par exemple afficher un message particulier à l’utilisateur, ouvrir un nouvel écran, remonter des analytics, etc.

Je vous propose ici le code complet du fragment qui présente les spécificités suivantes :

  • Il implémente l’interface OnMarkerClickListener.
  • Il place le listener au niveau de l’objet GoogleMap dans la méthode onMapReady.
  • Il positionne un marker sur Paris.
  • Il repère le clic sur le marker pour afficher un Toast.
public final class MainActivityFragment
  extends Fragment
  implements OnMapReadyCallback, OnMarkerClickListener
{

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
      final View rootView = inflater.inflate(R.layout.fragment_main, container, false);
      final MapFragment mapFragment = (MapFragment) getActivity().getFragmentManager().findFragmentById(R.id.map);
      mapFragment.getMapAsync(this);
      return rootView;
    }

    @Override
    public void onMapReady(GoogleMap googleMap)
    {
      googleMap.setOnMarkerClickListener(this);
      googleMap.addMarker(new MarkerOptions().position(new LatLng(48.8534100, 2.3488000)).title("Paris"));
    }

    @Override
    public boolean onMarkerClick(Marker marker)
    {
      if ("Paris".equals(marker.getTitle()))
      {
        Toast.makeText(getActivity().getApplicationContext(), "Paris", Toast.LENGTH_LONG).show();
      }

      return true;
    }
    
}

Après compilation du code et lancement de l’application, vous devriez normalement constater le résultat suivant après avoir cliqué sur l’unique marker de la carte :

Déplacer un marker

Je vous propose maintenant de voir comment il est possible de déplacer un marker. C’est en réalité très simple puisqu’il suffit d’utiliser la méthode draggable de la classe MarkerOptions. Cette méthode accepte un seul paramètre de type booléen. Aussi, il convient de renseigner la valeur true pour autoriser le déplacement du marker par les utilisateurs et false pour empêcher le déplacement du marker (il s’agit du comportement par défaut).

Par exemple, le morceau de code suivant permet de positionner un marker sur Paris, mais l’utilisateur peut le déplacer à tout moment en cliquant longtemps dessus :

@Override
public void onMapReady(GoogleMap googleMap)
{
  googleMap.addMarker(new MarkerOptions().position(new LatLng(48.8534100, 2.3488000)).draggable(true));
}

Maintenant que savons comment permettre à l’utilisateur de déplacer des markers, je vous propose de voir comment nous pouvons détecter le déplacement d’un marker. Pour cela, nous allons utiliser et implémenter l’interface OnMarkerDragListener. Cette interface doit ensuite être renseignée auprès de notre instance de GoogleMap grâce à la méthode setOnMarkerDragListener.

L’implémentation de cette interface, nous oblige à implémenter les trois méthodes suivantes :

@Override
public void onMarkerDragStart(Marker marker)
{
}

@Override
public void onMarkerDrag(Marker marker)
{
}

@Override
public void onMarkerDragEnd(Marker marker)
{
}

Le nom des trois méthodes est assez explicite :

  • La méthode onMarkerDragStart est appelée lorsque le déplacement du marker démarre.
  • La méthode onMarkerDrag est appelée de manière régulière tout au long du déplacement du marker.
  • La méthode onMarkerDragEnd est appelée quand le déplacement du marker est terminé.

Chacune de ces méthodes fournies le marker concerné par le déplacement en tant qu’unique paramètres. Libre à vous de l’exploiter.

Je vous propose ici le code complet du fragment qui présente les spécificités suivantes :

  • Il implémente l’interface OnMarkerDragListener.
  • Il place le listener au niveau de l’objet GoogleMap dans la méthode onMapReady.
  • Il positionne un marker sur Paris.
public final class PlaceholderFragment
  extends Fragment
  implements OnMapReadyCallback, OnMarkerDragListener
{

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
      final View rootView = inflater.inflate(R.layout.fragment_main, container, false);
      final MapFragment mapFragment = (MapFragment) getActivity().getFragmentManager().findFragmentById(R.id.map);
      mapFragment.getMapAsync(this);
      return rootView;
    }

    @Override
    public void onMapReady(GoogleMap googleMap)
    {
      googleMap.setOnMarkerDragListener(this);
      googleMap.addMarker(new MarkerOptions().position(new LatLng(48.8534100, 2.3488000)).draggable(true));
    }

    @Override
    public void onMarkerDragStart(Marker marker)
    {

    }

    @Override
    public void onMarkerDrag(Marker marker)
    {

    }

    @Override
    public void onMarkerDragEnd(Marker marker)
    {

    }
    
}

Premiers pas avec les infos windows

Je vous propose de terminer ce chapitre et cette première partie du tutoriel en revenant rapidement sur les infos windows. En effet, jusqu’à maintenant, nous savons qu’il est possible d’en afficher un automatiquement au clic sur un marker à condition d’avoir pris le temps de lui renseigner un titre et éventuellement un sous-titre, mais nous ne savons pour le moment :

  • ni les afficher ou les cacher manuellement,
  • ni détecter un clic dessus.

Je vous propose de corriger ces lacunes tout de suite.

Afficher ou cacher un info window

Afficher ou cacher un info window est en réalité très simple puisqu’il convient d’utiliser les méthodes suivantes de la classe Marker :

  • showInfoWindow pour afficher l’info window
  • hideInfoWindow pour cacher l’info window

Le seul problème que nous avons, c’est que si nous savons manipuler des MarkersOptions, nous avons pas encore vu comment récupérer le marker créé une fois celui-ci ajouté à la carte. En réalité, la méthode addMarker de la classe GoogleMap renvoie un objet de type Marker. Nous pouvons donc garder une référence sur ce Marker pour ensuite appeler les méthodes showInfoWindow et hideInfoWindow.

Par exemple, le code suivant permet d’afficher l’info window d’un marker placé sur Paris sans avoir besoin de cliquer dessus :

@Override
public void onMapReady(GoogleMap googleMap)
{
  final Marker marker = googleMap.addMarker(new MarkerOptions().position(new LatLng(48.8534100, 2.3488000)).title("Paris"));
  marker.showInfoWindow();
}

Si vous exécutez l’application, vous devriez bien voir l’info window sans avoir besoin de cliquer sur le marker comme en témoigne la capture d’écran ci-dessous :

Cliquer sur un info window

Maintenant que nous sommes en mesure d’afficher ou cacher manuellement un info window, je vous propose de voir comment il est possible de détecter le clic sur ces fameuses info bulles. A l’image de ce que nous avions mis en place pour détecter le clic sur les markers, nous allons devoir utiliser et implémenter une interface : OnInfoWindowClickListener. Cette interface doit ensuite être renseignée auprès de notre instance de GoogleMap grâce à la méthode setOnInfoWindowClickListener. L’implémentation de cette interface, nous oblige à implémenter la méthode onInfoWindowClick suivante :

@Override
public void onInfoWindowClick(Marker marker)
{

}

Comme vous pouvez le constater, cette méthode nous fournit, via son unique paramètre, le marker relié à l’info windows sur lequel l’utilisateur vient de cliquer.

Je vous propose ici le code complet du fragment qui présente les spécificités suivantes :

  • Il implémente l’interface OnInfoWindowClickListener.
  • Il place le listener au niveau de l’objet GoogleMap dans la méthode onMapReady.
  • Il positionne un marker sur Paris.
  • Il repère le clic sur l’info window pour afficher un Toast.
public final class PlaceholderFragment
  extends Fragment
  implements OnMapReadyCallback, OnInfoWindowClickListener
{

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
      final View rootView = inflater.inflate(R.layout.fragment_main, container, false);
      final MapFragment mapFragment = (MapFragment) getActivity().getFragmentManager().findFragmentById(R.id.map);
      mapFragment.getMapAsync(this);
      return rootView;
    }

    @Override
    public void onMapReady(GoogleMap googleMap)
    {
      googleMap.setOnInfoWindowClickListener(this);
      googleMap.addMarker(new MarkerOptions().position(new LatLng(48.8534100, 2.3488000)).title("Paris"));
    }

    @Override
    public void onInfoWindowClick(Marker marker)
    {
      if ("Paris".equals(marker.getTitle()) == true)
      {
        Toast.makeText(getActivity().getApplicationContext(), "Paris", Toast.LENGTH_LONG).show();
      }
    }
    
}

Après compilation du code et lancement de l’application, vous devriez normalement constater le résultat suivant après avoir cliqué sur l’info window de l’unique marker de la carte :

En résumé

  • Il est possible de placer de nombreux markers sur une carte.
  • Il est possible de personnaliser les markers.
  • Il est possible de tracker les intéractions de l’utilisateur avec les markers.
  • Il est possible de tracker les intéractions de l’utilisateur avec les info window.

Commentaires