Utilisation de l'interface dans le SDK Google Maps pour Android

Cet article sera utile à ceux qui n'ont pas déjà utilisé le SDK Google Maps dans leur travail.

Sous le chat, les méthodes de base pour travailler avec la carte sont décrites, telles que l'ajout et la gestion de marqueurs, les moyens de déplacer la caméra sur la carte, le contrôle du zoom, le tracé de l'itinéraire et le géocodage. Ainsi que les limitations et les moyens de les contourner.

image
Source

Pour écrire l'article, je me suis inspiré de ma propre expérience, que j'ai acquise en écrivant une demande de coursiers utilisant Google maps dans leur travail. Ainsi, toutes les captures d'écran et une éventuelle mention de la logique métier se produiront dans le contexte de la création d'une interface de messagerie.

Malheureusement, le SDK Google Maps pour Android ne vous permet pas de modifier la position des boutons de commande, les soi-disant Contrôles de l'interface utilisateur, notamment: IndoorLevelPicker - montrant le plan d'étage des bâtiments, Compass - la boussole, bouton Ma position - aller à la carte à l'emplacement actuel, barre d'outils Carte - boutons pour construire l'itinéraire et ouvrir la carte, ainsi que ZoomControls - augmenter et diminuer l'échelle de la carte.

En utilisant la barre d'outils Carte et ZoomControls à titre d'exemple, voyons quelles difficultés peuvent survenir en raison de l'impossibilité de changer la position des contrôles et comment contourner cela.

image
Problèmes d'affichage des contrôles d'interface utilisateur du SDK (surlignés en orange) et leurs homologues personnalisés (surlignés en vert)

Dans ce cas, nous avons dans le coin inférieur droit un bouton (bouton d'action flottant) pour naviguer vers la liste des adresses des bons de livraison, l'image de gauche montre que ZoomControls était en dessous et sont pratiquement inaccessibles à cliquer. Dans l'image de droite, lorsque vous cliquez sur le marqueur, des boutons de la barre d'outils Carte apparaissent, ils apparaissent également sous le bouton pour accéder à la liste des commandes.

Solution

La première chose que nous devons faire est de masquer l'affichage des boutons d'origine. Vous pouvez le faire en remplaçant la méthode onMapReady, elle est appelée lorsque la carte est prête à l'emploi.

Ne pas afficher les boutons Contrôle du zoom et ne pas afficher les boutons pour créer un itinéraire à partir du SDK
private GoogleMap mMap; private UiSettings uiSettings; @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; uiSettings = mMap.getUiSettings(); //   Zoom uiSettings.setZoomControlsEnabled(false); //         SDK uiSettings.setMapToolbarEnabled(false); } 


Nous ajoutons les boutons nécessaires à la mise en page, où ils devraient être conformes à notre conception:

image
Emplacement des boutons de contrôle de carte personnalisés

Ensuite, dans la méthode onCreateView, spécifiez les actions qui doivent se produire lorsque nos boutons sont cliqués:

Gestionnaires des boutons pour augmenter et diminuer l'échelle, ainsi que pour construire un itinéraire
 private ImageButton imageButtonZoomIn; private ImageButton imageButtonZoomOut; private ImageButton imageButtonRoute; private GoogleMap mMap; @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { //  imageButtonZoomIn = view.findViewById(R.id.imageButtonZoomIn); imageButtonZoomIn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mMap.animateCamera(CameraUpdateFactory.zoomIn()); } }); //  imageButtonZoomOut = view.findViewById(R.id.imageButtonZoomOut); imageButtonZoomOut.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mMap.animateCamera(CameraUpdateFactory.zoomOut()); } }); //      imageButtonRoute = view.findViewById(R.id.imageButtonRoute); imageButtonRoute.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String latitude = String.valueOf(activMarker.getPosition().latitude); String longitude = String.valueOf(activMarker.getPosition().longitude); Uri gmmIntentUri = Uri.parse("google.navigation:q=" + latitude + "," + longitude); Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); mapIntent.setPackage("com.google.android.apps.maps"); try{ if (mapIntent.resolveActivity(Objects.requireNonNull(getActivity()).getPackageManager()) != null) { startActivity(mapIntent); } }catch (NullPointerException e){ Log.e(TAG, "onClick: NullPointerException: Couldn't open map." + e.getMessage() ); Toast.makeText(getActivity(), "Couldn't open map", Toast.LENGTH_SHORT).show(); } } }); } 


La particularité de la méthode animateCamera est que l'échelle change en douceur, et pas instantanément, et si, par exemple, vous devez désactiver l'animation d'un bouton de zoom spécifique lorsque l'échelle maximale ou minimale est atteinte, vous devez redéfinir la méthode onCameraIdle , qui est appelée lorsque l'échelle de la carte change.

Activation et désactivation des boutons de zoom
  @Override public void onCameraIdle() { if (mMap.getCameraPosition().zoom == mMap.getMinZoomLevel()){ //  ,     imageButtonZoomOut.setEnabled(false); imageButtonZoomIn.setEnabled(true); }else if (mMap.getCameraPosition().zoom == mMap.getMaxZoomLevel()){ //  ,     imageButtonZoomOut.setEnabled(true); imageButtonZoomIn.setEnabled(false); }else { //       imageButtonZoomOut.setEnabled(true); imageButtonZoomIn.setEnabled(true); } } 


Pour effectuer une action avec un marqueur (à l'exception du glisser-déposer), par exemple, créez une nouvelle commande, supprimez un marqueur placé au hasard, accédez à une commande existante ou appelez le téléphone comme indiqué dans la commande, ajoutez les boutons de contrôle appropriés à la mise en page et enregistrez leurs gestionnaires.

image
Boutons de contrôle des marqueurs

Traitement d'un clic sur une carte pour ajouter un marqueur et afficher des boutons de contrôle personnalisés
 private GoogleMap mMap; private ImageButton imageButtonRoute; @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() { @Override public void onMapClick(LatLng latLng) { imageButtonRoute.setVisibility(View.GONE); if (myMarker !=null){ //   myMarker.remove(); } //    myMarker = mMap.addMarker(new MarkerOptions() .position(latLng) //   .title(Objects.requireNonNull(getContext()).getString(R.string.title_on_marker_to_new_order)) // true ,         .draggable(true)); myMarker.setTag(null); //    imageButtonAddMarker.setVisibility(View.VISIBLE); imageButtonRemoveMarker.setVisibility(View.VISIBLE); } }); } 


Nous indiquons ce que nous voulons faire avec le marqueur lorsque nous cliquons sur le bouton Ajouter une commande
 private ImageButton imageButtonAddMarker; private Marker myMarker; @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { imageButtonAddMarker = view.findViewById(R.id.imageButtonAddMarker); imageButtonAddMarker.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (myMarker !=null && myMarker.getTag()==null) { //    imageButtonAddMarker.setVisibility(View.GONE); imageButtonRemoveMarker.setVisibility(View.GONE); //  ,          listener.openOrderContentsFragmentFromMap(null, myMarker); //   ,     myMarker.remove(); } } }); } 


Une autre caractéristique est qu'il n'y a pas de bouton dans le SDK pour supprimer le marqueur sur la carte. Pour ce faire, nous créons également notre propre bouton:

Spécifiez ce que nous voulons faire avec le marqueur lorsque nous cliquons sur le bouton Supprimer le marqueur
 private ImageButton imageButtonRemoveMarker; private Marker myMarker; @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { imageButtonRemoveMarker = view.findViewById(R.id.imageButtonRemoveMarker); imageButtonRemoveMarker.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (myMarker !=null && myMarker.getTag()==null){ //   imageButtonAddMarker.setVisibility(View.GONE); imageButtonRemoveMarker.setVisibility(View.GONE); //    myMarker.remove(); } } }); } 


Lorsque vous cliquez sur le marqueur, son titre s'ouvre, en cliquant sur celui qui peut également être utilisé pour effectuer une action, lorsque je clique sur le titre d'un nouveau marqueur, je crée une nouvelle commande pour livraison au service de messagerie et sur le marqueur d'une commande existante j'ouvre des informations de livraison détaillées, y compris une liste marchandises.

Définissez l'action lorsque vous cliquez sur la fenêtre d'informations sur le marqueur
 private GoogleMap mMap; @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; mMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() { @Override public void onInfoWindowClick(Marker marker) { if (marker.getTag()==null) { //    : //      listener.openOrderContentsFragmentFromMap(null, marker); if (myMarker != null) { //   ,     myMarker.remove(); } } else { //     : //         listener.openOrderContentsFragment((Long) marker.getTag()); } } }); } 


Le processus de sortie de plusieurs marqueurs (lire la liste des commandes) sur la carte n'est pas différent en principe de la sortie d'un marqueur. Un marqueur se compose de coordonnées (position), d'un titre (titre), d'un petit texte sous un titre (extrait) et d'une balise (setTag) - il peut être utilisé pour identifier de nombreux marqueurs sur la carte.

image
Plusieurs marqueurs de carte

Dessiner plusieurs marqueurs avec des coordonnées données
 public void drawListMarker(List<InfoMarker> latLngList) { if (latLngList == null || latLngList.size() == 0) { return; } //    mMap.clear(); LatLngBounds.Builder builder = new LatLngBounds.Builder(); boolean fiarstGreean = true; int count = 1; for (InfoMarker latLng : latLngList) { BitmapDescriptor icon; if (fiarstGreean){ //      //..           icon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN); fiarstGreean = false; } else { icon = latLng.getIcon(); } //      mMap.addMarker(new MarkerOptions().position(latLng.getLatLng()).title(String.valueOf(count)).snippet(latLng.getTitle()).icon(icon)).setTag(latLng.getIdOrder()); builder.include(latLng.getLatLng()); count++; } //       CameraUpdate cameraUpdate; if (loaded) { cameraUpdate = CameraUpdateFactory .newLatLngBounds(builder.build(), 100); } else { cameraUpdate = CameraUpdateFactory.newLatLng(builder.build().getCenter()); } mMap.moveCamera(cameraUpdate); mMap.animateCamera(CameraUpdateFactory.zoomIn()); mMap.animateCamera(CameraUpdateFactory.zoomTo(10), 1000, null); } 


Quelques mots sur le géocodeur

Un géocodeur est utilisé pour obtenir une adresse basée sur des coordonnées. En plaçant un marqueur sur la carte et en cliquant sur le bouton Ajouter une commande, nous obtenons les coordonnées géographiques du point souhaité, c'est-à-dire latitude et longitude. Mais pour la commodité de l'utilisateur, il sera agréable de montrer l'adresse sous une forme lisible par l'homme, c'est-à-dire, par exemple, pays, ville, rue, maison.

Le SDK Google Maps contient la classe Geocoder , en appelant sa méthode getFromLocation , vous pouvez obtenir un tableau d'adresses aux coordonnées spécifiées.

Afin de ne pas bloquer le thread d'interface utilisateur pendant longtemps, surtout si Internet est lent ou inaccessible, par des appels - nous utiliserons RxJava:

image
L'adresse du point résultant sur la carte en fonction des coordonnées géographiques

Utilisation de Java RX pour accéder au géocodeur Google
 LatLng position = myMarker.getPosition(); Location location = new Location("new"); location.setLatitude(position.latitude); location.setLongitude(position.longitude); LocationRepostiory locationRepostiory = new LocationRepostiory(context, location); locationRepostiory.getLastLocation(). observeOn(SchedulerProvider.getInstance().ui()). subscribeOn(SchedulerProvider.getInstance().computation()). subscribe(locationString -> { if(editTextAddress.length()==0){ //       ,      editTextAddress.setText(locationString); } }, throwable -> { }); 


Le texte de la classe LocationRepostiory dans laquelle se produit le géocodage inversé
 public class LocationRepostiory { private Context context; private Location location; public LocationRepostiory(Context context, Location location) { this.context = context; this.location = location; } public Single<String> getLastLocation() { return Single.create(this::subscribeOnLocation); } private void subscribeOnLocation(SingleEmitter<String> e) { Geocoder geocoder = new Geocoder(context, Locale.getDefault()); String errorMessage = ""; List<Address> addresses = null; try { //     addresses = geocoder.getFromLocation( location.getLatitude(), location.getLongitude(), // In this sample, get just a single address. 1); } catch (IOException ioException) { //,      I/O. errorMessage = context.getString(R.string.service_not_available); Log.e(TAG, errorMessage, ioException); } catch (IllegalArgumentException illegalArgumentException) { // ,       errorMessage = context.getString(R.string.invalid_lat_long_used); Log.e(TAG, errorMessage + ". " + "Latitude = " + location.getLatitude() + ", Longitude = " + location.getLongitude(), illegalArgumentException); } //  ,     if (addresses == null || addresses.size() == 0) { if (errorMessage.isEmpty()) { errorMessage = context.getString(R.string.no_address_found); Log.e(TAG, errorMessage); } } else { Address address = addresses.get(0); ArrayList<String> addressFragments = new ArrayList<String>(); //      . for (int i = 0; i <= address.getMaxAddressLineIndex(); i++) { e.onSuccess(address.getAddressLine(i)); } } } } 

Source: https://habr.com/ru/post/fr484100/


All Articles