Zaawansowane Androida w Kotlin 04.1: Mapy Google na Androida

To ćwiczenie programowania jest częścią kursu „Android dla zaawansowanych w Kotlinie”. Korzyści z tego kursu będą dla Ciebie najbardziej wartościowe, jeśli wykonasz je w sekwencjach ćwiczeń z programowania, ale nie jest to obowiązkowe. Wszystkie ćwiczenia z kursu są wymienione na stronie Zaawansowane ćwiczenia z programowania na Androida w Kotlin.

Tworzenie aplikacji w Mapach Google pozwala dodawać do aplikacji funkcje takie jak zdjęcia satelitarne, rozbudowane opcje interfejsu dla map, śledzenie lokalizacji i znaczniki lokalizacji. Możesz dodać wartość do standardowych Map Google, wyświetlając informacje z własnego zbioru danych, np. informacje o lokalizacjach popularnych łowisk lub ścianek wspinaczkowych. Możesz też tworzyć gry, w których gracz musi odkrywać świat fizyczny, np. poszukiwanie skarbów lub gra w rzeczywistości rozszerzonej.

W ramach tej lekcji tworzysz aplikację Mapy Google o nazwie Wander, która wyświetla mapy niestandardowe i pokazuje lokalizację użytkownika.

Wymagania wstępne

Wiedza na temat:

  • Jak utworzyć podstawową aplikację na Androida i uruchomić ją w Android Studio.
  • jak tworzyć zasoby (np. ciągi tekstowe) i nimi zarządzać;
  • Jak refaktoryzować zmienne kodu i zmieniać ich nazwy, korzystając z Androida Studio.
  • Jak korzystać z mapy Google jako użytkownika.
  • Jak ustawić uprawnienia czasu działania.

Czego się nauczysz

  • Jak uzyskać klucz interfejsu API z Konsoli interfejsów API Google i zarejestrować klucz w aplikacji
  • Jak zintegrować Mapę Google z aplikacją
  • Jak wyświetlać różne typy map
  • Styl mapy Google
  • Jak dodawać znaczniki do mapy
  • Jak zezwolić użytkownikowi na umieszczenie znacznika w ciekawym miejscu (ważnym)
  • Jak włączyć śledzenie lokalizacji
  • Tworzenie aplikacji Wander z wbudowaną Mapą Google
  • Tworzenie niestandardowych funkcji aplikacji, takich jak znaczniki i styl
  • Jak włączyć śledzenie lokalizacji w aplikacji

W ramach tych ćwiczeń tworzysz aplikację Wander, która wyświetla mapę Google ze spersonalizowanym stylem. Aplikacja Wander umożliwia upuszczanie znaczników na lokalizacje, dodawanie nakładek oraz wyświetlanie lokalizacji w czasie rzeczywistym.

Pakiet SDK dla Map na Androida wymaga klucza interfejsu API. Aby uzyskać klucz interfejsu API, zarejestruj swój projekt na stronie Interfejs API i usługi. Klucz interfejsu API jest powiązany z certyfikatem cyfrowym, który łączy aplikację z autorem. Więcej informacji o używaniu certyfikatów cyfrowych i podpisywaniu aplikacji znajdziesz w artykule Podpisywanie aplikacji.

W tym kursie wykorzystasz klucz interfejsu API certyfikatu debugowania. Zgodnie z opisem w artykule Podpisywanie kompilacji debugowania certyfikat debugowania nie jest bezpieczny. Opublikowane aplikacje na Androida, które używają pakietu SDK Map na Androida, wymagają drugiego klucza interfejsu API: klucza certyfikatu wersji. Więcej informacji na temat uzyskiwania certyfikatu wersji znajdziesz w artykule Uzyskiwanie klucza interfejsu API.

Android Studio zawiera szablon aktywności w Mapach Google, który generuje przydatny kod szablonu. Kod szablonu zawiera plik google_maps_api.xml z linkiem, który upraszcza uzyskanie klucza interfejsu API.

Krok 1. Utwórz projekt Wander na podstawie szablonu map

  1. Utwórz nowy projekt Android Studio.
  2. Wybierz szablon Aktywność w Mapach Google.

  1. Nazwij projekt Wander.
  2. Ustaw minimalny poziom API na API 19. Upewnij się, że językiem jest Kotlin.
  3. Kliknij Zakończ.
  4. Gdy aplikacja będzie gotowa, zajrzyj do swojego projektu i tych plików związanych z mapami, które Android Studio tworzy dla Ciebie:

google_maps_api.xml – ten plik konfiguracji służy do przechowywania klucza interfejsu API. Szablon generuje dwa pliki google_maps_api.xml – jeden na potrzeby debugowania i jeden na potrzeby wersji. Plik klucza interfejsu API certyfikatu debugowania znajduje się w katalogu src/debug/res/values. Plik klucza interfejsu API certyfikatu wersji znajduje się w katalogu src/release/res/values. W tym ćwiczeniu z programowania korzystasz tylko z certyfikatu debugowania.

activity_maps.xml – plik układu zawiera jeden fragment, który wypełnia cały ekran. Klasa SupportMapFragment jest podklasą klasy Fragment. SupportMapFragment to najprostsza metoda umieszczania mapy w aplikacji. Otoka kodu wokół widoku mapy, która automatycznie spełnia niezbędne potrzeby w cyklu życia.

Możesz uwzględnić atrybut SupportMapFragment w pliku układu za pomocą tagu <fragment> w dowolnym tagu ViewGroup z dodatkowym atrybutem name.

android:name="com.google.android.gms.maps.SupportMapFragment"

MapsActivity.java – plik MapsActivity.kt tworzy instancję SupportMapFragment w metodzie onCreate() i używa klasy (getMapAsync()), aby automatycznie inicjować system map i widok. Aktywność zawierająca SupportMapFragment musi implementować interfejs OnMapReadyCallback oraz metodę onMapReady() tego interfejsu. Metoda onMapReady() jest wywoływana podczas ładowania mapy.

Krok 2. Uzyskaj klucz interfejsu API

  1. Otwórz wersję debugowania pliku google_maps_api.xml.
  2. W pliku znajdź komentarz z długim adresem URL. Parametry adresu URL zawierają konkretne informacje o aplikacji.
  3. Skopiuj adres URL i wklej go w przeglądarce.
  4. Postępuj zgodnie z wyświetlanymi instrukcjami, by utworzyć projekt na stronie Interfejsy API i usługi. Ze względu na parametry podane w adresie URL strona wie, że ma automatycznie włączyć pakiet Maps SDK na Androida.
  5. Kliknij Create API API (Utwórz klucz interfejsu API).
  6. Na następnej stronie przejdź do sekcji Klucze API i kliknij utworzony przed chwilą klucz.
  7. Kliknij Ogranicz klucz i wybierz Mapy SDK na Androida, by ograniczyć użycie klucza do aplikacji na Androida.
  8. Skopiuj wygenerowany klucz interfejsu API. Zaczyna się od "AIza".
  9. W pliku google_maps_api.xml wklej klucz do ciągu google_maps_key w miejscu YOUR_KEY_HERE.
  10. Uruchom aplikację. W swojej aktywności powinna pojawić się osadzona mapa ze znacznikiem ustawionym w Sydney w Australii. Znacznik Sydney jest częścią szablonu i możesz go później zmienić.

Krok 3. Zmień nazwę mMap

MapsActivity ma prywatny lateinit var o nazwie mMap, który jest typu GoogleMap. Aby zachować zgodność z konwencjami nazewnictwa w Kotlinie, zmień nazwę z mMap na map.

  1. W aplikacji MapsActivity kliknij prawym przyciskiem myszy mMap i wybierz Refaktor i Zmień nazwę...

  1. Zmień nazwę zmiennej na map.

Zwróć uwagę, że wszystkie odwołania do mMap w funkcji onMapReady() zmieniają się również na map.

Mapy Google udostępniają kilka typów mapy: normalne, hybrydowe, satelitarne, terenowe oraz "none" (w ogóle nie są dostępne).

Normalna mapa

Mapa satelitarna

Mapa hybrydowa

Mapa terenu

Każdy typ mapy dostarcza innych rodzajów informacji. Na przykład podczas korzystania z nawigacji w Mapach Google warto wyświetlić nazwy ulic, aby skorzystać z opcji normalnej. Mapa ukształtowania terenu może ułatwić Ci określenie, ile jeszcze szczytów dotrzeć, aby dotrzeć na szczyt.

W tym zadaniu:

  1. Dodawanie paska aplikacji z menu opcji, który umożliwia zmianę typu mapy
  2. Przesuń lokalizację początkową na mapie do swojej lokalizacji domowej.
  3. Dodaj obsługę znaczników, które wskazują pojedyncze lokalizacje na mapie i mogą obejmować etykiety.

Dodaj menu typów map

W tym kroku dodasz pasek aplikacji z menu opcji, które pozwolą użytkownikowi zmienić typ mapy.

  1. Aby utworzyć nowy plik XML menu, kliknij prawym przyciskiem myszy katalog res i wybierz Nowy plik zasobów Androida.
  2. W oknie dialogowym nadaj plikowi nazwę map_options.
  3. Jako typ zasobu wybierz Menu.
  4. Kliknij OK.
  5. Zastąp kod w nowym pliku na karcie Kod tym kodem, aby utworzyć opcje menu mapy. Typ „"none"” jest pomijany, ponieważ "none" powoduje brak mapy. Ten krok powoduje błąd, ale można go rozwiązać w następnym.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
   <item
       android:id="@+id/normal_map"
       android:title="@string/normal_map"
       app:showAsAction="never" />
   <item
       android:id="@+id/hybrid_map"
       android:title="@string/hybrid_map"
       app:showAsAction="never" />
   <item
       android:id="@+id/satellite_map"
       android:title="@string/satellite_map"
       app:showAsAction="never" />
   <item
       android:id="@+id/terrain_map"
       android:title="@string/terrain_map"
       app:showAsAction="never" />
</menu>
  1. W strings.xml dodaj zasoby dla atrybutów title, aby naprawić błędy.
<resources>
   ...
   <string name="normal_map">Normal Map</string>
   <string name="hybrid_map">Hybrid Map</string>
   <string name="satellite_map">Satellite Map</string>
   <string name="terrain_map">Terrain Map</string>
   <string name="lat_long_snippet">Lat: %1$.5f, Long: %2$.5f</string>
   <string name="dropped_pin">Dropped Pin</string>
   <string name="poi">poi</string>
</resources>
  1. W MapsActivity zastąp metodę onCreateOptionsMenu() i zawyż menu w pliku zasobów map_options.
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
   val inflater = menuInflater
   inflater.inflate(R.menu.map_options, menu)
   return true
}
  1. W MapsActivity.kt zastąp metodę onOptionsItemSelected(). Zmień typ mapy za pomocą stały typu mapy, by odzwierciedlić wybór użytkownika.
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
   // Change the map type based on the user's selection.
   R.id.normal_map -> {
       map.mapType = GoogleMap.MAP_TYPE_NORMAL
       true
   }
   R.id.hybrid_map -> {
       map.mapType = GoogleMap.MAP_TYPE_HYBRID
       true
   }
   R.id.satellite_map -> {
       map.mapType = GoogleMap.MAP_TYPE_SATELLITE
       true
   }
   R.id.terrain_map -> {
       map.mapType = GoogleMap.MAP_TYPE_TERRAIN
       true
   }
   else -> super.onOptionsItemSelected(item)
}
  1. Uruchom aplikację.
  2. Kliknij , aby zmienić typ mapy. Zwróć uwagę na to, jak wygląd mapy zmienia się w różnych trybach.

Domyślnie wywołanie zwrotne onMapReady() zawiera kod, który umieszcza znacznik w Sydney w Australii, gdzie utworzono Mapy Google. Domyślne wywołanie zwrotne będzie też animować mapę, by przesunąć ją do Sydney.

W tym zadaniu kamera przesuwa mapę do Twojego domu, zbliża się do określonego poziomu i umieszcza w nim znacznik.

Krok 1. Powiększ widok domu i dodaj znacznik

  1. W pliku MapsActivity.kt znajdź metodę onMapReady(). Usuń znajdujący się w nim kod, który powoduje umieszczenie znacznika w Sydney i przesuwa kamerę. Tak powinna teraz wyglądać Twoja metoda.
override fun onMapReady(googleMap: GoogleMap) {
   map = googleMap

}
  1. Aby uzyskać szerokość i długość geograficzną domu, wykonaj te instrukcje.
  2. Utwórz wartość szerokości i długości geograficznej, a następnie podaj wartości zmiennoprzecinkowe.
val latitude = 37.422160
val longitude = -122.084270
  1. Utwórz nowy obiekt LatLng o nazwie homeLatLng. W obiekcie homeLatLng przekaż nowo utworzone wartości.
val homeLatLng = LatLng(latitude, longitude)
  1. Utwórz model val określający, jak chcesz powiększyć mapę. Użyj poziomu powiększenia 15f.
val zoomLevel = 15f

Poziom powiększenia steruje powiększeniem mapy. Na poniższej liście znajdziesz informacje o poziomie szczegółowości na poszczególnych poziomach powiększenia:

  • 1: świat
  • 5: ląd/kontynent
  • 10: miasto
  • 15: ulice
  • 20: budynki
  1. Przenieś aparat do kamery homeLatLng, wywołując funkcję moveCamera() w obiekcie map i przekazując obiekt CameraUpdate przy użyciu CameraUpdateFactory.newLatLngZoom(). Przekaż obiekt homeLatLng i zoomLevel.
map.moveCamera(CameraUpdateFactory.newLatLngZoom(homeLatLng, zoomLevel))
  1. Dodaj znacznik do mapy na homeLatLng.
map.addMarker(MarkerOptions().position(homeLatLng))

Ostateczna metoda powinna wyglądać tak:

override fun onMapReady(googleMap: GoogleMap) {
   map = googleMap

   //These coordinates represent the latitude and longitude of the Googleplex.
   val latitude = 37.422160
   val longitude = -122.084270
   val zoomLevel = 15f

   val homeLatLng = LatLng(latitude, longitude)
   map.moveCamera(CameraUpdateFactory.newLatLngZoom(homeLatLng, zoomLevel))
   map.addMarker(MarkerOptions().position(homeLatLng))
}
  1. Uruchom swoją aplikację. Mapa powinna przesunąć się po domu, powiększyć odpowiedni poziom i umieścić znacznik w domu.

Krok 2. Zezwól użytkownikom na dodawanie znacznika długim kliknięciem

W tym kroku dodajesz znacznik, gdy użytkownik dotknie lokalizacji na mapie i ją przytrzyma.

  1. Utwórz zaktualizowaną metodę w MapsActivity o nazwie setMapLongClick(), która przyjmuje GoogleMap jako argument.
  2. Podłącz detektor setOnMapLongClickListener do obiektu mapy.
private fun setMapLongClick(map:GoogleMap) {
   map.setOnMapLongClickListener { }
}
  1. W setOnMapLongClickListener() wywołaj metodę addMarker(). Nowy obiekt MarkerOptions z ustawioną pozycją LatLng.
private fun setMapLongClick(map: GoogleMap) {
   map.setOnMapLongClickListener { latLng ->
       map.addMarker(
           MarkerOptions()
               .position(latLng)
       )
   }
}
  1. Na końcu metody onMapReady() wywołaj setMapLongClick() za pomocą wywołania map.
override fun onMapReady(googleMap: GoogleMap) {
   ...
  
   setMapLongClick(map)
}
  1. Uruchom aplikację.
  2. Naciśnij i przytrzymaj mapę, aby umieścić znacznik w wybranej lokalizacji.
  3. Kliknij znacznik, aby wyśrodkować go na ekranie.

Krok 3. Dodaj okno informacyjne dla znacznika

W tym kroku dodasz kolumnę InfoWindow, w której wyświetlane są współrzędne znacznika po kliknięciu.

  1. W setMapLongClick()setOnMapLongClickListener() utwórz val dla użytkownika snippet. Fragment to dodatkowy tekst wyświetlany po tytule. Fragment kodu zawiera szerokość i długość geograficzną znacznika.
private fun setMapLongClick(map: GoogleMap) {
   map.setOnMapLongClickListener { latLng ->
       // A snippet is additional text that's displayed after the title.
       val snippet = String.format(
           Locale.getDefault(),
           "Lat: %1$.5f, Long: %2$.5f",
           latLng.latitude,
           latLng.longitude
       )
       map.addMarker(
           MarkerOptions()
               .position(latLng)
       )
   }
}
  1. W polu addMarker() ustaw title znacznika znacznika „Pinezka”, korzystając z zasobu ciągu R.string.dropped_pin.
  2. Ustaw znacznik snippet na snippet.

Ukończona funkcja wygląda tak:

private fun setMapLongClick(map: GoogleMap) {
   map.setOnMapLongClickListener { latLng ->
       // A Snippet is Additional text that's displayed below the title.
       val snippet = String.format(
           Locale.getDefault(),
           "Lat: %1$.5f, Long: %2$.5f",
           latLng.latitude,
           latLng.longitude
       )
       map.addMarker(
           MarkerOptions()
               .position(latLng)
               .title(getString(R.string.dropped_pin))
               .snippet(snippet)
              
       )
   }
}
  1. Uruchom aplikację.
  2. Naciśnij i przytrzymaj mapę, by upuścić znacznik lokalizacji.
  3. Kliknij znacznik, aby wyświetlić okno informacyjne.

Krok 4. Dodaj detektor ważnych miejsc

Domyślnie na mapie wyświetlane są ciekawe miejsca wraz z odpowiednimi ikonami. Ciekawe miejsca obejmują parki, szkoły, budynki administracji publicznej oraz inne obiekty. Po ustawieniu typu mapy na normal zobaczysz na niej ciekawe miejsca. Ciekawe miejsca reprezentują firmy, takie jak sklepy, restauracje czy hotele.

W tym kroku dodasz do mapy obiekt GoogleMap.OnPoiClickListener. Detektor kliknięć wstawia znacznik na mapie natychmiast po kliknięciu ważnego miejsca. Detektor kliknięć wyświetla też okno informacyjne z nazwą ciekawego miejsca.

  1. Utwórz zaktualizowaną metodę w MapsActivity o nazwie setPoiClick(), która przyjmuje GoogleMap jako argument.
  2. W metodzie setPoiClick() ustaw OnPoiClickListener w przekazywanym GoogleMap.
private fun setPoiClick(map: GoogleMap) {
   map.setOnPoiClickListener { poi ->

   }
}
  1. W setOnPoiClickListener() utwórz val poiMarker dla znacznika .
  2. Ustaw na nim znacznik za pomocą map.addMarker(), gdzie MarkerOptions ustawia title na nazwę ciekawego miejsca.
private fun setPoiClick(map: GoogleMap) {
   map.setOnPoiClickListener { poi ->
       val poiMarker = map.addMarker(
           MarkerOptions()
               .position(poi.latLng)
               .title(poi.name)
       )
   }
}
  1. W funkcji setOnPoiClickListener() wywołaj showInfoWindow() poiMarker, by od razu wyświetlić okno informacyjne.
poiMarker.showInfoWindow()

Końcowy kod funkcji setPoiClick() powinien wyglądać tak.

private fun setPoiClick(map: GoogleMap) {
   map.setOnPoiClickListener { poi ->
       val poiMarker = map.addMarker(
           MarkerOptions()
               .position(poi.latLng)
               .title(poi.name)
       )
       poiMarker.showInfoWindow()
   }
}
  1. Pod koniec onMapReady() zadzwoń do: setPoiClick() i przekaż w ciągu map.
override fun onMapReady(googleMap: GoogleMap) {
   ...

   setPoiClick(map)
}
  1. Uruchom aplikację i znajdź ciekawe miejsce, np. park lub kawiarnię.
  2. Kliknij ważne miejsce, aby umieścić na nim znacznik, a w oknie informacyjnym wyświetlić jego nazwę.

Mapy Google możesz dostosować na wiele sposobów, aby nadać swojej mapie niepowtarzalny wygląd.

Obiekt MapFragment możesz dostosować za pomocą dostępnych atrybutów XML, tak jak w przypadku każdego innego fragmentu. Na tym etapie dostosujesz wygląd i sposób działania treści elementu MapFragment, korzystając z metod znajdujących się na obiekcie GoogleMap.

Aby utworzyć własny styl mapy, generujesz plik JSON określający sposób wyświetlania obiektów na mapie. Nie musisz ręcznie tworzyć tego pliku JSON. Google udostępnia kreator stylu Map Google, który generuje kod JSON po zmianie wyglądu mapy. W tym zadaniu stylizujesz mapę w stylu retro, co oznacza, że mapa wykorzystuje stare kolory i dodajesz kolorowe drogi.

Krok 1: Utwórz styl mapy

  1. W przeglądarce otwórz stronę https://mapstyle.withgoogle.com/.
  2. Wybierz Utwórz styl.
  3. Wybierz Retro.

  1. Kliknij Więcej opcji.

  1. Z listy Typ funkcji wybierz Droga & Wypełnienie.
  2. Zmień kolor dróg na dowolny wybrany (np. różowy).

  1. Kliknij Zakończ.

  1. Skopiuj kod JSON z wyświetlonego okna i opcjonalnie zapisz go w postaci zwykłego tekstu do użycia w następnym kroku.

Krok 2. Dodaj styl do mapy

  1. W Android Studio w katalogu res utwórz katalog zasobów i nadaj mu nazwę raw. używasz zasobów katalogu raw, takich jak kod JSON;
  2. Utwórz plik (res/raw) o nazwie map_style.json.
  3. Wklej otrzymany kod JSON do nowego pliku zasobów.
  4. W narzędziu MapsActivity utwórz zmienną klasy TAG nad metodą onCreate(). Te dane służą do logowania.
private val TAG = MapsActivity::class.java.simpleName
  1. W narzędziu MapsActivity utwórz też funkcję setMapStyle(), która przyjmuje właściwość GoogleMap.
  2. W setMapStyle() dodaj blok try{}.
  3. W bloku try{} utwórz val success dla sukcesu. Dodajesz ten blok.
  4. W bloku try{} ustaw styl JSON na mapę, wywołaj setMapStyle() przy obiekcie GoogleMap. przekazać obiekt MapStyleOptions, który wczytuje plik JSON;
  5. Przypisz wynik do gestu success. Metoda setMapStyle() zwraca wartość logiczną oznaczającą stan analizy pliku stylu i określenia stylu.
private fun setMapStyle(map: GoogleMap) {
   try {
       // Customize the styling of the base map using a JSON object defined
       // in a raw resource file.
       val success = map.setMapStyle(
           MapStyleOptions.loadRawResourceStyle(
               this,
               R.raw.map_style
           )
       )
   }
}
  1. Dodaj instrukcję if (fałsz) dla success. Jeśli styl nie powiedzie się, wydrukuj dziennik z informacją o niepowodzeniu analizy.
private fun setMapStyle(map: GoogleMap) {
   try {
       ...
       if (!success) {
           Log.e(TAG, "Style parsing failed.")
       }
   }
}
  1. Dodaj blok catch{}, aby rozwiązać problem z brakującym plikiem stylu. Jeśli w bloku catch nie można wczytać pliku, wywołaj Resources.NotFoundException.
private fun setMapStyle(map: GoogleMap) {
   try {
       ...
   } catch (e: Resources.NotFoundException) {
       Log.e(TAG, "Can't find style. Error: ", e)
   }
}

Ukończona metoda powinna wyglądać tak:

private fun setMapStyle(map: GoogleMap) {
   try {
       // Customize the styling of the base map using a JSON object defined
       // in a raw resource file.
       val success = map.setMapStyle(
           MapStyleOptions.loadRawResourceStyle(
               this,
               R.raw.map_style
           )
       )

       if (!success) {
           Log.e(TAG, "Style parsing failed.")
       }
   } catch (e: Resources.NotFoundException) {
       Log.e(TAG, "Can't find style. Error: ", e)
   }
}
  1. Na koniec wywołaj metodę setMapStyle() w metodzie onMapReady() przekazującą obiekt GoogleMap.
override fun onMapReady(googleMap: GoogleMap) {
   ...
   setMapStyle(map)
}
  1. Uruchom aplikację.
  2. Ustaw tryb mapy na normal, a nowy styl powinien być widoczny w stylu retro i przez wybrane przez Ciebie drogi.

Krok 3. Dostosuj styl znacznika

Mapę możesz spersonalizować, stosując styl znaczników mapy. W tym kroku zmieniasz domyślne czerwone znaczniki na bardziej charakterystyczne.

  1. W metodzie onMapLongClick() dodaj poniższy wiersz kodu do elementu MarkerOptions() konstruktora, aby użyć znacznika domyślnego, ale zmień kolor na niebieski.
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE))

Teraz onMapLongClickListener() wygląda tak:

map.setOnMapLongClickListener { latLng ->
   // A snippet is additional text that's displayed after the title.
   val snippet = String.format(
       Locale.getDefault(),
       "Lat: %1$.5f, Long: %2$.5f",
       latLng.latitude,
       latLng.longitude
   )
   map.addMarker(
       MarkerOptions()
           .position(latLng)
           .title(getString(R.string.dropped_pin))
           .snippet(snippet)
         .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE))
   )
}
  1. Uruchom aplikację. Znaczniki, które pojawią się po długim kliknięciu, będą teraz cieniowane na niebiesko. Znaczniki użytecznych miejsc są nadal czerwone, ponieważ styl nie został dodany do metody onPoiClick().

Jednym ze sposobów dostosowywania mapy Google jest rysowanie na niej. Ta technika jest przydatna, gdy chcesz wyróżnić określony typ lokalizacji, na przykład popularne łowiska.

  • Kształty: możesz dodawać do mapy linie, wielokąty i okręgi.
  • Obiekty GroundOverlay: nakładka gruntowa to obraz ustalony na mapie. W przeciwieństwie do znaczników nakładki na powierzchni są kierowane na powierzchnię Ziemi, a nie na ekran. Obracanie, pochylanie lub powiększanie mapy zmienia orientację zdjęcia. Nakładki na powierzchni są przydatne, gdy chcesz poprawić jeden obraz na jednym obszarze na mapie.

Krok: dodaj warstwę naziemną

W tym zadaniu dodasz nakładkę domową w postaci kształtu Androida do swojej lokalizacji domowej.

  1. Pobierz ten obraz Androida i zapisz go w folderze res/drawable. Sprawdź, czy nazwa pliku to android.png.

  1. Po wywołaniu funkcji onMapReady() przenieś kamerę w pozycję domu, utwórz obiekt GroundOverlayOptions.
  2. Przypisz obiekt do zmiennej o nazwie androidOverlay.
val androidOverlay = GroundOverlayOptions()
  1. Użyj metody BitmapDescriptorFactory.fromResource(), aby utworzyć obiekt BitmapDescriptor z pobranego zasobu graficznego.
  2. Przekaż otrzymany obiekt BitmapDescriptor do metody image() obiektu GroundOverlayOptions.
val androidOverlay = GroundOverlayOptions()
   .image(BitmapDescriptorFactory.fromResource(R.drawable.android))
  1. Utwórz wartość float overlaySize dla szerokości wybranej nakładki. W tym przykładzie sprawdza się szerokość 100f.

Ustaw właściwość position obiektu GroundOverlayOptions, wywołując metodę position(), a następnie przekaż obiekt homeLatLng i overlaySize.

val overlaySize = 100f
val androidOverlay = GroundOverlayOptions()
   .image(BitmapDescriptorFactory.fromResource(R.drawable.android))
   .position(homeLatLng, overlaySize)
  1. Wywołaj addGroundOverlay() obiektu GoogleMap i przekaż go w obiekcie GroundOverlayOptions.
map.addGroundOverlay(androidOverlay)
  1. Uruchom aplikację.
  2. Zmień wartość zoomLevel na 18f, aby zobaczyć obraz Androida jako nakładkę.

Użytkownicy często używają Map Google do sprawdzania swojej bieżącej lokalizacji. Aby wyświetlić lokalizację urządzenia na mapie, możesz użyć warstwy danych-lokalizacji.

Warstwa danych o lokalizacji dodaje do mapy opcję Moja lokalizacja. Gdy użytkownik naciśnie przycisk, mapa zostanie wyśrodkowana na urządzeniu. Lokalizacja jest widoczna jako niebieska kropka, jeśli urządzenie jest nieruchome, lub jako niebieski szewron, gdy urządzenie się porusza.

W tym zadaniu włączysz warstwę danych-lokalizacji.

Krok: poproś o dostęp do lokalizacji

Włączenie śledzenia lokalizacji w Mapach Google wymaga pojedynczego wiersza kodu. Musisz się jednak upewnić, że użytkownik przyznał dostęp do lokalizacji (korzystając z modelu uprawnień czasu działania).

W tym kroku poprosisz o dostęp do lokalizacji i włączysz śledzenie lokalizacji.

  1. W pliku AndroidManifest.xml sprawdź, czy uprawnienia FINE_LOCATION już są obecne. Android Studio wstawił te uprawnienia, gdy wybierałeś szablon Map Google.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  1. W MapsActivity utwórz zmienną typu REQUEST_LOCATION_PERMISSION.
private val REQUEST_LOCATION_PERMISSION = 1
  1. Aby sprawdzić, czy uprawnienia zostały przyznane, utwórz w MapsActivity metodę o nazwie isPermissionGranted(). W ramach tej metody sprawdź, czy użytkownik przyznał odpowiednie uprawnienia.
private fun isPermissionGranted() : Boolean {
  return ContextCompat.checkSelfPermission(
       this,
      Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
}
  1. Aby włączyć śledzenie lokalizacji w aplikacji, utwórz w MapsActivity metodę o nazwie enableMyLocation(), która nie podaje żadnych argumentów i nie zwraca niczego. Sprawdź, czy masz uprawnienie ACCESS_FINE_LOCATION. Jeśli tak, włącz warstwę lokalizacji. W przeciwnym razie poproś o zgodę.
private fun enableMyLocation() {
   if (isPermissionGranted()) {
       map.isMyLocationEnabled = true 
   }
   else {
       ActivityCompat.requestPermissions(
           this,
           arrayOf<String>(Manifest.permission.ACCESS_FINE_LOCATION),
           REQUEST_LOCATION_PERMISSION
       )
   }
}
  1. Wywołaj enableMyLocation() z wywołaniem zwrotnym onMapReady(), aby włączyć warstwę lokalizacji.
override fun onMapReady(googleMap: GoogleMap) {
   ...
   enableMyLocation()
}
  1. Zastąp metodę onRequestPermissionsResult(). Jeśli uprawnienie requestCode jest równe REQUEST_LOCATION_PERMISSION, a tablica grantResults nie jest pusta z PackageManager.PERMISSION_GRANTED w pierwszym boksie, wywołaj enableMyLocation().
override fun onRequestPermissionsResult(
   requestCode: Int,
   permissions: Array<String>,
   grantResults: IntArray) {
   if (requestCode == REQUEST_LOCATION_PERMISSION) {
       if (grantResults.contains(PackageManager.PERMISSION_GRANTED)) {
           enableMyLocation()
       }
   }
}
  1. Uruchom aplikację. Powinno pojawić się okno z prośbą o dostęp do lokalizacji urządzenia. Zezwól na to.

Na mapie wyświetla się bieżąca lokalizacja urządzenia za pomocą niebieskiej kropki. Zwróć uwagę na przycisk lokalizacji. Jeśli przesuniesz mapę i klikniesz ten przycisk, wyśrodkujesz mapę z powrotem na urządzeniu.

Pobierz kod ukończonych ćwiczeń z programowania.

$  git clone https://github.com/googlecodelabs/android-kotlin-geo-maps


Możesz też pobrać repozytorium jako plik ZIP, rozpakować go i otworzyć w Android Studio.

Pobierz plik ZIP

  • Aby korzystać z interfejsu API Map Google, potrzebujesz klucza interfejsu API z Konsoli interfejsów API Google.
  • W Androidzie Studio użycie szablonu aktywności w Mapach Google powoduje wygenerowanie Activity z pojedynczym SupportMapFragment w układzie aplikacji. Szablon dodaje również plik ACCESS_FINE_PERMISSION do pliku manifestu aplikacji i implementuje OnMapReadyCallback w aktywności, zastępując wymaganą metodę onMapReady().

Aby zmienić typ mapy GoogleMap w czasie działania, użyj metody GoogleMap.setMapType(). Mapą Google może być jeden z tych typów map:

  • Normalny: typowa mapa drogi. Pokazuje drogi, niektóre obiekty utworzone przez ludzi, a także ważne obiekty przyrodnicze, takie jak rzeki. Widoczne są również etykiety dróg i obiektów.
  • Hybrydowa: dane zdjęć satelitarnych z dodanymi mapami drogowymi. Widoczne są również etykiety dróg i obiektów.
  • Satelita: dane zdjęcia. Etykiety dróg i obiektów nie są widoczne.
  • Teren: dane topograficzne. Na mapie znajdują się kolory, linie konturów i etykiety oraz cieniowanie perspektywy. Widoczne są również niektóre drogi i etykiety.
  • Brak: brak podstawowych kafelków mapy.

Informacje o Mapach Google:

  • Znacznik jest wskaźnikiem określonej lokalizacji geograficznej.
  • Po dotknięciu domyślny znacznik wyświetla okno informacyjne z informacjami o lokalizacji.
  • Domyślnie na mapie podstawowej wyświetlane są ciekawe miejsca wraz z odpowiednimi ikonami. Ciekawe miejsca obejmują parki, szkoły, budynki administracji publicznej oraz inne obiekty.
  • Dodatkowo zdjęcia ważnych miejsc (sklepy, restauracje, hotele i inne) domyślnie wyświetlają się na mapie, gdy typ mapy to normal.
  • Za pomocą OnPoiClickListener możesz przechwytywać kliknięcia ważnych miejsc.
  • Używając kreatora stylu, możesz zmienić wygląd prawie wszystkich elementów mapy Google. Kreator stylu wygeneruje plik JSON, który przekażesz do Map Google za pomocą metody setMapStyle().
  • Możesz dostosować znaczniki, zmieniając kolor domyślny lub zastępując domyślną ikonę znacznika własnym obrazem.

Inne ważne informacje:

  • Aby dopasować obraz do położenia geograficznego, użyj nakładki na powierzchni.
  • Użyj obiektu GroundOverlayOptions, aby określić obraz, jego rozmiar w metrach i pozycję jego obrazu. Przekaż ten obiekt do metody GoogleMap.addGroundOverlay(), aby ustawić nakładkę na mapie.
  • Jeśli Twoja aplikacja ma uprawnienia ACCESS_FINE_LOCATION, możesz włączyć śledzenie lokalizacji, ustawiając map.isMyLocationEnabled = true.
  • Nie obejmuje on tych ćwiczeń z programowania, ale możesz podać dodatkowe informacje o danej lokalizacji, korzystając z Google Street View – zdjęcia panoramicznego danego miejsca, po którym można się poruszać.

Dokumentacja dla programistów Androida:

Dokumentacja referencyjna:

Linki do innych ćwiczeń z programowania w tym kursie znajdziesz na stronie Zaawansowane ćwiczenia z Androida w Kotlin.