Arbeiten mit der Oberfläche im Google Maps SDK für Android

Dieser Artikel ist hilfreich für Benutzer, die das Google Maps SDK noch nicht in ihrer Arbeit verwendet haben.

Unter der Katze werden die grundlegenden Methoden zum Arbeiten mit der Karte beschrieben, z. B. das Hinzufügen und Verwalten von Markierungen, das Bewegen der Kamera über die Karte, das Steuern des Zooms, das Zeichnen einer Route und das Geokodieren. Sowie Einschränkungen und Möglichkeiten, diese zu umgehen.

Bild
Quelle

Beim Schreiben des Artikels habe ich mich von meinen eigenen Erfahrungen inspirieren lassen, die ich bei der Erstellung einer Bewerbung für Kuriere mit Google Maps gesammelt habe. Daher werden alle Screenshots und eine mögliche Erwähnung der Geschäftslogik im Zusammenhang mit dem Aufbau einer Kurier-Schnittstelle erstellt.

Leider erlaubt Ihnen das Google Maps SDK für Android nicht, die Position der Steuertasten zu ändern, die sogenannten Steuerelemente der Benutzeroberfläche: IndoorLevelPicker - zeigt den Grundriss von Gebäuden an, Kompass - den Kompass, Schaltfläche "Mein Standort" - zeigt die Karte am aktuellen Standort an, Karten-Symbolleiste - Schaltflächen zum Erstellen der Route und Öffnen der Karte sowie Zoom-Steuerelemente - vergrößern und verkleinern den Kartenmaßstab.

Sehen wir uns am Beispiel der Kartensymbolleiste und der Zoom-Steuerelemente an, welche Schwierigkeiten auftreten können, wenn die Position der Steuerelemente nicht geändert werden kann, und wie Sie dies umgehen können.

Bild
Probleme beim Anzeigen von UI-Steuerelementen aus dem SDK (orange hervorgehoben) und deren benutzerdefinierten Gegenstücken (grün hervorgehoben)

In diesem Fall haben wir in der unteren rechten Ecke eine Schaltfläche (schwebende Aktionstaste), um zur Liste der Adressen von Lieferaufträgen zu gelangen. In der Abbildung auf der linken Seite können Sie sehen, dass ZoomControls darunter waren und praktisch nicht zum Klicken zugänglich sind. Wenn Sie in der rechten Abbildung auf die Markierung klicken, werden Schaltflächen in der Kartensymbolleiste angezeigt. Sie werden auch unter der Schaltfläche angezeigt, um zur Liste der Bestellungen zu gelangen.

Lösung

Das erste, was wir tun müssen, ist die Anzeige der ursprünglichen Schaltflächen zu verbergen. Sie können dies tun, indem Sie die onMapReady-Methode überschreiben. Sie wird aufgerufen, wenn die Karte einsatzbereit ist.

Keine Schaltflächen anzeigen Zoomsteuerung und keine Schaltflächen anzeigen, um eine Route aus dem SDK zu erstellen
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); } 


Wir fügen dem Layout die erforderlichen Schaltflächen hinzu, die unserem Design entsprechen sollen:

Bild
Position der benutzerdefinierten Schaltflächen zur Kartensteuerung

Geben Sie dann in der onCreateView-Methode die Aktionen an, die beim Klicken auf unsere Schaltflächen ausgeführt werden sollen:

Handler für die Schaltflächen zum Vergrößern und Verkleinern des Maßstabs sowie zum Erstellen einer Route
 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(); } } }); } 


Die Besonderheit der animateCamera- Methode besteht darin, dass sich der Maßstab reibungslos und nicht sofort ändert. Wenn Sie beispielsweise die Animation einer bestimmten Zoom-Schaltfläche deaktivieren müssen, wenn der maximale oder minimale Maßstab erreicht ist, müssen Sie die onCameraIdle- Methode neu definieren, die aufgerufen wird, wenn sich der Kartenmaßstab ändert.

Ein- und Ausschalten der Zoomtasten
  @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); } } 


Um eine Aktion mit einer Markierung auszuführen (außer Ziehen und Ablegen), erstellen Sie beispielsweise eine neue Bestellung, löschen Sie eine zufällig platzierte Markierung, wechseln Sie zu einer vorhandenen Bestellung oder rufen Sie das Telefon an, wie in der Bestellung angegeben, fügen Sie die entsprechenden Steuerschaltflächen zum Layout hinzu und registrieren Sie die Handler.

Bild
Marker-Steuertasten

Verarbeiten eines Klicks auf eine Karte, um eine Markierung hinzuzufügen und benutzerdefinierte Steuerungsschaltflächen anzuzeigen
 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); } }); } 


Wir geben an, was wir mit dem Marker tun möchten, wenn wir auf die Schaltfläche Bestellung hinzufügen klicken
 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(); } } }); } 


Ein weiteres Merkmal ist, dass im SDK keine Schaltfläche zum Löschen der Markierung auf der Karte vorhanden ist. Dazu machen wir auch unseren eigenen Button:

Legen Sie fest, was mit dem Marker geschehen soll, wenn Sie auf die Schaltfläche Marker löschen klicken
 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(); } } }); } 


Wenn ich auf den Marker klicke, öffnet sich sein Titel. Wenn ich auf den Titel eines neuen Markers klicke, erstelle ich eine neue Bestellung für die Lieferung an den Kurier und auf den Marker einer bestehenden Bestellung öffne ich detaillierte Lieferinformationen, einschließlich einer Liste Waren.

Legen Sie die Aktion fest, wenn Sie auf das Marker-Informationsfenster klicken
 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()); } } }); } 


Der Vorgang der Ausgabe mehrerer Marker (Liste der Bestellungen lesen) auf die Karte unterscheidet sich grundsätzlich nicht von der Ausgabe eines Markers. Ein Marker besteht aus Koordinaten (Position), einem Titel (Titel), einem kleinen Text unter einem Titel (Snippet) und einem Tag (setTag) - er kann verwendet werden, um viele Marker auf der Karte zu identifizieren.

Bild
Mehrere Kartenmarkierungen

Zeichnen mehrerer Marker mit angegebenen Koordinaten
 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); } 


Ein paar Worte zum Geocoder

Ein Geocodierer wird verwendet, um eine Adresse basierend auf Koordinaten zu erhalten. Wenn Sie eine Markierung auf die Karte setzen und auf die Schaltfläche zum Hinzufügen der Reihenfolge klicken, erhalten Sie die geografischen Koordinaten des gewünschten Punkts, d. H. Breitengrad und Längengrad. Für den Benutzer ist es jedoch angenehm, die Adresse in einer für den Menschen lesbaren Form anzuzeigen, d. H. Zum Beispiel Land, Stadt, Straße, Haus.

Das Google Maps SDK enthält die Geocoder- Klasse. Durch Aufrufen der getFromLocation- Methode können Sie ein Array von Adressen an den angegebenen Koordinaten abrufen .

Um den UI-Thread nicht für lange Zeit zu blockieren, insbesondere wenn das Internet durch Aufrufe langsam oder unzugänglich ist, verwenden wir RxJava:

Bild
Die resultierende Punktadresse auf der Karte basiert auf geografischen Koordinaten

Verwenden von Java RX für den Zugriff auf Google Geocoder
 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 -> { }); 


Der Text der LocationRepostiory-Klasse, in der die umgekehrte Geokodierung erfolgt
 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/de484100/


All Articles