Auf einer Karte den aktuellen Ort auswählen und Details anzeigen

In dieser Anleitung wird beschrieben, wie du den aktuellen Standort eines Android-Geräts ermitteln und zu diesem Ort (einem Geschäft oder POI) Details anzeigen kannst. Folge einfach dieser Anleitung, um mit dem Maps SDK for Android, dem Places SDK for Android und dem Anbieter für kombinierte Standortbestimmung in den Standort-APIs der Google Play-Dienste eine Android-App zu erstellen.

Code abrufen

Klone das Google Maps Android API Version 2-Beispielrepository (Google Maps Android API v2 Samples) von GitHub oder lade es herunter.

Entwicklungsprojekt einrichten

Führe die folgenden Schritte aus, um das Anleitungsprojekt in Android Studio zu erstellen.

  1. Lade Android Studio herunter und installiere die Anwendung.
  2. Füge das Paket Google Play-Dienste zu Android Studio hinzu.
  3. Klone das Repository "Google Maps Android API v2 Samples" oder lade es herunter, sofern du das noch nicht zu Beginn dieser Anleitung getan hast.
  4. Anleitungsprojekt importieren:

    • Wähle in Android Studio File > New > Import Project aus.
    • Gehe zu dem Speicherort, an dem du das Repository "Google Maps Android API v2 Samples" nach dem Download gespeichert hast.
    • Suche dort nach dem Projekt CurrentPlaceDetailsOnMap:
      PATH-TO-SAVED-REPO/android-samples/tutorials/CurrentPlaceDetailsOnMap
    • Wähle das Projektverzeichnis aus und klicke auf OK. In Android Studio wird dein Projekt jetzt mit dem Build-Tool Gradle erstellt.

API-Schlüssel abrufen und die erforderlichen APIs aktivieren

Als letzten Schritt benötigst du noch einen Google API-Schlüssel, der eine Berechtigung zum Verwenden von Maps SDK for Android und Places SDK for Android hat.

Klicke hier, um einen Schlüssel anzufordern und die APIs zu aktivieren.

Jetzt starten

Weitere Informationen findest du in der vollständigen Anleitung zum Abrufen eines API-Schlüssels.

API-Schlüssel in App einfügen

  1. Bearbeite die gradle.properties-Datei deines Projekts.
  2. Füge deinen API-Schlüssel in den Wert der Eigenschaft GOOGLE_MAPS_API_KEY ein:

    GOOGLE_MAPS_API_KEY=PASTE-YOUR-API-KEY-HERE

    Wenn du deine App erstellst, wird der API-Schlüssel von Gradle kopiert und in das Android-Manifest der App eingefügt. Die Datei build.gradle der App enthält die folgende Zeile, mit der der String google_maps_key im Manifest der Gradle-Eigenschaft GOOGLE_MAPS_API_KEY zugeordnet wird:

    resValue "string", "google_maps_key",
            (project.findProperty("GOOGLE_MAPS_API_KEY") ?: "")
    

App erstellen und ausführen

  1. Verbinde ein Android-Gerät mit deinem Computer. Befolge die Anleitung, um Entwickleroptionen auf deinem Android-Gerät zu aktivieren und dein System so zu konfigurieren, dass das Gerät erkannt wird. Alternativ kannst du mit Android Virtual Device (AVD) Manager ein virtuelles Gerät konfigurieren. Beim Auswählen eines Emulators solltest du ein Image verwenden, das die Google-APIs enthält. Weitere Informationen findest du im Startleitfaden.
  2. Klicke in Android Studio auf die Menüoption Run (oder das Abspielsymbol). Wähle ein Gerät aus, wenn du dazu aufgefordert wirst.

In Android Studio wird Gradle aufgerufen, um die App zu erstellen. Dann wird die App auf dem Gerät oder im Emulator ausgeführt. Nun sollte eine Karte mit einer Reihe von Markierungen erscheinen, die um deinen aktuellen Standort herum angeordnet sind.

Fehlerbehebung:

  • Wenn du keine Karte siehst, überprüfe, ob du einen API-Schlüssel erhalten und ihn wie oben beschrieben in die App eingefügt hast. Kontrolliere, ob es im Protokoll im Android Monitor von Android Studio Fehlermeldungen zum API-Schlüssel gibt.
  • Wenn die Karte nur eine einzelne Markierung auf der Sydney Harbour Bridge anzeigt (dem in der App angegebenen Standardstandort), prüfe, ob du der App eine Berechtigung zur Standortermittlung zugewiesen hast. Die App fordert die Berechtigung zur Standortermittlung zur Laufzeit an. Dabei folgt sie dem Muster im Leitfaden zu Android-Berechtigungen. Du kannst Berechtigungen auch direkt auf dem Gerät festlegen, indem du Einstellungen > Apps > App-Name > Berechtigungen > Standort auswählst. Ausführliche Informationen zum Umgang mit Berechtigungen in deinem Code findest du unten im Abschnitt Berechtigung zur Standortermittlung anfordern.
  • Verwende die Fehlerbehebungstools in Android Studio, um Protokolle aufzurufen und Fehler in der App zu beheben.

Code verstehen

In diesem Teil der Anleitung werden die wesentlichsten Teile der App CurrentPlaceDetailsOnMap erläutert, damit du besser verstehst, wie du eine ähnliche App erstellst.

Places API-Clients instanziieren

Die folgenden Schnittstellen sind primäre Einstiegspunkte für das Places SDK for Android:

  • Der GeoDataClient bietet Zugriff auf die Google-Datenbank, die Informationen zu lokalen Orten und Geschäften enthält.
  • Der PlaceDetectionClient bietet schnellen Zugriff auf den aktuellen Ort des Geräts und die Möglichkeit, den genauen Standort eines Gerät an einem bestimmten Ort zu melden.

Die Schnittstelle LocationServices ist der Haupteinstiegspunkt für Android-Standortdienste.

Um die APIs zu verwenden, musst du GeoDataClient, PlaceDetectionClient und FusedLocationProviderClient in der Methode onCreate() deines Fragments oder deiner Aktivität instanziieren, so wie im folgenden Codebeispiel:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Construct a GeoDataClient.
    mGeoDataClient = Places.getGeoDataClient(this, null);

    // Construct a PlaceDetectionClient.
    mPlaceDetectionClient = Places.getPlaceDetectionClient(this, null);

    // Construct a FusedLocationProviderClient.
    mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
}

Berechtigung zur Standortermittlung anfordern

Um den Gerätestandort zu bestimmen und die Funktion zum Antippen der Schaltfläche Mein Standort auf der Karte für Nutzer freizugeben, muss über deine App eine Berechtigung zur Standortermittlung angefordert werden.

Diese Anleitung enthält den Code, den du zum Anfordern einer detaillierten Berechtigung zur Standortermittlung benötigst. Weitere Informationen findest du im Leitfaden zu Android-Berechtigungen.

  1. Füge die Berechtigung als untergeordnetes Element des Elements <manifest> in deinem Android-Manifest hinzu:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.currentplacedetailsonmap">
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    </manifest>
    
  2. Fordere in deiner App Laufzeitberechtigungen an und gib dem Nutzer die Möglichkeit, die Berechtigung zur Standortermittlung zuzulassen oder abzulehnen. Mit dem folgenden Code wird überprüft, ob der Nutzer eine gültige Berechtigung zur Standortermittlung erteilt hat. Falls nicht, wird die Berechtigung angefordert:

    private void getLocationPermission() {
        /*
         * Request location permission, so that we can get the location of the
         * device. The result of the permission request is handled by a callback,
         * onRequestPermissionsResult.
         */
        if (ContextCompat.checkSelfPermission(this.getApplicationContext(),
                android.Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            mLocationPermissionGranted = true;
        } else {
            ActivityCompat.requestPermissions(this,
                    new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
                    PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
        }
    }
    
  3. Überschreibe den Rückruf onRequestPermissionsResult(), um das Ergebnis der Berechtigungsanfrage zu verarbeiten:

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        mLocationPermissionGranted = false;
        switch (requestCode) {
            case PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    mLocationPermissionGranted = true;
                }
            }
        }
        updateLocationUI();
    }
    

    In einem späteren Abschnitt dieser Anleitung wird die Methode updateLocationUI() beschrieben.

Karte hinzufügen

Rufe eine Karte mit dem Maps SDK for Android auf.

  1. Füge der Layoutdatei activity_maps.xml deiner Aktivität ein <fragment>-Element hinzu. Mit diesem Element wird ein SupportMapFragment definiert, das als Container für die Karte fungiert und Zugriff auf das GoogleMap-Objekt bietet. In der Anleitung wird die Version der Android Support Library des Kartenfragments verwendet, um die Abwärtskompatibilität mit früheren Versionen des Android-Frameworks sicherzustellen.

    <fragment xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/map"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.example.currentplacedetailsonmap.MapsActivityCurrentPlace" />
    
    
  2. Lege in der onCreate()-Methode die Layoutdatei als Inhaltsansicht fest:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
    }
    
  3. Implementiere die Schnittstelle OnMapReadyCallback und überschreibe die onMapReady()-Methode, um die Karte einzurichten, wenn das GoogleMap-Objekt verfügbar ist:

    public void onMapReady(GoogleMap map) {
        mMap = map;
    
        // Do other setup activities here too, as described elsewhere in this tutorial.
    
        // Turn on the My Location layer and the related control on the map.
        updateLocationUI();
    
        // Get the current location of the device and set the position of the map.
        getDeviceLocation();
    }
    
  4. Rufe in der Methode onCreate() deiner Aktivität einen Ziehpunkt zum Kartenfragment ab, indem du FragmentManager.findFragmentById() aufrufst. Registriere dich dann mit getMapAsync() für den Karten-Callback:

    SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
            .findFragmentById(R.id.map);
    mapFragment.getMapAsync(this);
    
  5. Schreibe eine updateLocationUI()-Methode, um die Standortsteuerelemente auf der Karte festzulegen. Wenn der Nutzer die Berechtigung zur Standortermittlung erteilt hat, aktiviere die Ebene "Mein Standort" und das entsprechende Steuerelement auf der Karte. Deaktiviere andernfalls die Ebene und das Steuerelement und setze den aktuellen Standort auf NULL.

    private void updateLocationUI() {
        if (mMap == null) {
            return;
        }
        try {
            if (mLocationPermissionGranted) {
                mMap.setMyLocationEnabled(true);
                mMap.getUiSettings().setMyLocationButtonEnabled(true);
            } else {
                mMap.setMyLocationEnabled(false);
                mMap.getUiSettings().setMyLocationButtonEnabled(false);
                mLastKnownLocation = null;
                getLocationPermission();
            }
        } catch (SecurityException e)  {
            Log.e("Exception: %s", e.getMessage());
        }
    }
    

Standort des Android-Geräts abrufen und Karte positionieren

Verwende den Anbieter für kombinierte Standortbestimmung, um den zuletzt bekannten Standort des Geräts zu finden, und nutze den Standort dann, um die Karte zu positionieren. Die Anleitung enthält den Code, den du benötigst. Weitere Informationen zum Abrufen des Gerätestandorts findest du im Leitfaden für den Anbieter für kombinierte Standortbestimmung in den Standort-APIs der Google Play-Dienste.

private void getDeviceLocation() {
    /*
     * Get the best and most recent location of the device, which may be null in rare
     * cases when a location is not available.
     */
    try {
        if (mLocationPermissionGranted) {
            Task locationResult = mFusedLocationProviderClient.getLastLocation();
            locationResult.addOnCompleteListener(this, new OnCompleteListener() {
                @Override
                public void onComplete(@NonNull Task task) {
                    if (task.isSuccessful()) {
                        // Set the map's camera position to the current location of the device.
                        mLastKnownLocation = task.getResult();
                        mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
                                new LatLng(mLastKnownLocation.getLatitude(),
                                        mLastKnownLocation.getLongitude()), DEFAULT_ZOOM));
                    } else {
                        Log.d(TAG, "Current location is null. Using defaults.");
                        Log.e(TAG, "Exception: %s", task.getException());
                        mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(mDefaultLocation, DEFAULT_ZOOM));
                        mMap.getUiSettings().setMyLocationButtonEnabled(false);
                    }
                }
            });
        }
    } catch(SecurityException e)  {
        Log.e("Exception: %s", e.getMessage());
    }
}

Aktuellen Ort abrufen

Verwende das Places SDK for Android, um eine Liste von Orten abzurufen, die sich am aktuellen Standort befinden. In diesem Zusammenhang ist ein Ort ein Geschäft oder ein anderer POI.

In dieser Anleitung wird der aktuelle Ort abgerufen, wenn der Nutzer auf die Schaltfläche Get Place (Ort abrufen) klickt. Dem Nutzer wird eine Liste mit möglichen Orten zur Auswahl angezeigt. Dann wird an der Position des ausgewählten Orts eine Markierung erstellt. Die Anleitung enthält den Code, den du für die Interaktion mit dem Places SDK for Android benötigst. Weitere Informationen findest du in der Anleitung zum Abrufen des aktuellen Orts.

  1. Erstelle eine Layoutdatei (current_place_menu.xml) für das Optionsmenü und überschreibe die Methode onCreateOptionsMenu(), um das Optionsmenü einzurichten. Entsprechenden Code findest du in der zugehörigen Beispiel-App.
  2. Überschreibe die Methode onOptionsItemSelected(), um den aktuellen Ort abzurufen, wenn der Nutzer auf die Option Get Place (Ort abrufen) klickt:
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.option_get_place) {
            showCurrentPlace();
        }
        return true;
    }
    
  3. Erstelle eine showCurrentPlace()-Methode, um eine Liste mit Orten abzurufen, die sich am aktuellen Standort befinden:

    private void showCurrentPlace() {
        if (mMap == null) {
            return;
        }
    
        if (mLocationPermissionGranted) {
            // Use fields to define the data types to return.
            List<Place.Field> placeFields = Arrays.asList(Place.Field.NAME, Place.Field.ADDRESS,
                    Place.Field.LAT_LNG);
    
            // Use the builder to create a FindCurrentPlaceRequest.
            FindCurrentPlaceRequest request =
                    FindCurrentPlaceRequest.newInstance(placeFields);
    
            // Get the likely places - that is, the businesses and other points of interest that
            // are the best match for the device's current location.
            @SuppressWarnings("MissingPermission") final
            Task<FindCurrentPlaceResponse> placeResult =
                    mPlacesClient.findCurrentPlace(request);
            placeResult.addOnCompleteListener (new OnCompleteListener<FindCurrentPlaceResponse>() {
                @Override
                public void onComplete(@NonNull Task<FindCurrentPlaceResponse> task) {
                    if (task.isSuccessful() && task.getResult() != null) {
                        FindCurrentPlaceResponse likelyPlaces = task.getResult();
    
                        // Set the count, handling cases where less than 5 entries are returned.
                        int count;
                        if (likelyPlaces.getPlaceLikelihoods().size() < M_MAX_ENTRIES) {
                            count = likelyPlaces.getPlaceLikelihoods().size();
                        } else {
                            count = M_MAX_ENTRIES;
                        }
    
                        int i = 0;
                        mLikelyPlaceNames = new String[count];
                        mLikelyPlaceAddresses = new String[count];
                        mLikelyPlaceAttributions = new List[count];
                        mLikelyPlaceLatLngs = new LatLng[count];
    
                        for (PlaceLikelihood placeLikelihood : likelyPlaces.getPlaceLikelihoods()) {
                            // Build a list of likely places to show the user.
                            mLikelyPlaceNames[i] = placeLikelihood.getPlace().getName();
                            mLikelyPlaceAddresses[i] = placeLikelihood.getPlace().getAddress();
                            mLikelyPlaceAttributions[i] = placeLikelihood.getPlace()
                                    .getAttributions();
                            mLikelyPlaceLatLngs[i] = placeLikelihood.getPlace().getLatLng();
    
                            i++;
                            if (i > (count - 1)) {
                                break;
                            }
                        }
    
                        // Show a dialog offering the user the list of likely places, and add a
                        // marker at the selected place.
                        MapsActivityCurrentPlace.this.openPlacesDialog();
                    }
                    else {
                        Log.e(TAG, "Exception: %s", task.getException());
                    }
                }
            });
        } else {
            // The user has not granted permission.
            Log.i(TAG, "The user did not grant location permission.");
    
            // Add a default marker, because the user hasn't selected a place.
            mMap.addMarker(new MarkerOptions()
                    .title(getString(R.string.default_info_title))
                    .position(mDefaultLocation)
                    .snippet(getString(R.string.default_info_snippet)));
    
            // Prompt the user for permission.
            getLocationPermission();
        }
    }
    
  4. Generiere eine openPlacesDialog()-Methode, um ein Formular anzuzeigen, über das der Nutzer einen Ort aus einer Liste dieser Orte auswählen kann. Erstelle auf der Karte eine Markierung für den ausgewählten Ort. Die Markierung enthält den Namen und die Adresse des Orts sowie alle Quellenangaben, die von der API bereitgestellt werden:

    private void openPlacesDialog() {
        // Ask the user to choose the place where they are now.
        DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // The "which" argument contains the position of the selected item.
                LatLng markerLatLng = mLikelyPlaceLatLngs[which];
                String markerSnippet = mLikelyPlaceAddresses[which];
                if (mLikelyPlaceAttributions[which] != null) {
                    markerSnippet = markerSnippet + "\n" + mLikelyPlaceAttributions[which];
                }
    
                // Add a marker for the selected place, with an info window
                // showing information about that place.
                mMap.addMarker(new MarkerOptions()
                        .title(mLikelyPlaceNames[which])
                        .position(markerLatLng)
                        .snippet(markerSnippet));
    
                // Position the map's camera at the location of the marker.
                mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(markerLatLng,
                        DEFAULT_ZOOM));
            }
        };
    
        // Display the dialog.
        AlertDialog dialog = new AlertDialog.Builder(this)
                .setTitle(R.string.pick_place)
                .setItems(mLikelyPlaceNames, listener)
                .show();
    }
    
  5. Erstelle ein benutzerdefiniertes Layout für den Inhalt des Infofensters. Dadurch erhältst du in diesem Fenster mehrere Inhaltszeilen. Füge zuerst die XML-Layoutdatei custom_info_contents.xml hinzu, die eine Textansicht für den Titel des Infofensters enthält, und dann eine weitere Textansicht für das Snippet, also den Textinhalt dieses Fensters.

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layoutDirection="locale"
        android:orientation="vertical">
        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:textColor="#ff000000"
            android:textStyle="bold" />
    
        <TextView
            android:id="@+id/snippet"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#ff7f7f7f" />
    </LinearLayout>
    
    
  6. Implementiere die Schnittstelle InfoWindowAdapter, um ein Layout-Inflating durchzuführen und den Inhalt des Infofensters zu laden.

    @Override
    public void onMapReady(GoogleMap map) {
        // Do other setup activities here too, as described elsewhere in this tutorial.
        mMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
    
        @Override
        // Return null here, so that getInfoContents() is called next.
        public View getInfoWindow(Marker arg0) {
            return null;
        }
    
        @Override
        public View getInfoContents(Marker marker) {
            // Inflate the layouts for the info window, title and snippet.
            View infoWindow = getLayoutInflater().inflate(R.layout.custom_info_contents, null);
    
            TextView title = ((TextView) infoWindow.findViewById(R.id.title));
            title.setText(marker.getTitle());
    
            TextView snippet = ((TextView) infoWindow.findViewById(R.id.snippet));
            snippet.setText(marker.getSnippet());
    
            return infoWindow;
          }
        });
    }
    

Status der Karte speichern

Speichere die Kameraposition und den Gerätestandort. Wenn ein Nutzer ein Android-Gerät dreht oder die Konfiguration ändert, wird die Kartenaktivität durch das Android-Framework zerstört und wieder neu erstellt. Damit dieser Ablauf flüssiger wird, solltest du den Status der relevanten Anwendung speichern und bei Bedarf wiederherstellen.

In dieser Anleitung findest du den gesamten Code, der erforderlich ist, um den Status der Karte zu speichern. Weitere Informationen findest du in der Anleitung zum savedInstanceState-Paket.

  1. Richte in deiner Kartenaktivität Schlüssel/Wert-Paare ein, um den Status der Aktivität zu speichern:

    private static final String KEY_CAMERA_POSITION = "camera_position";
    private static final String KEY_LOCATION = "location";
    
  2. Implementiere den onSaveInstanceState()-Callback, um den Status zu speichern, wenn die Aktivität pausiert:

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        if (mMap != null) {
            outState.putParcelable(KEY_CAMERA_POSITION, mMap.getCameraPosition());
            outState.putParcelable(KEY_LOCATION, mLastKnownLocation);
            super.onSaveInstanceState(outState);
        }
    }
    
  3. Rufe in der onCreate()-Methode deiner Aktivität den Standort des Geräts und die Kameraposition der Karte ab, falls sie zuvor gespeichert wurde:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            mCurrentLocation = savedInstanceState.getParcelable(KEY_LOCATION);
            mCameraPosition = savedInstanceState.getParcelable(KEY_CAMERA_POSITION);
        }
        ...
    }