Kotlin 04.1 기반 Android 고급: Android Google 지도

이 Codelab은 Kotlin 기반 Android 고급 교육 과정의 일부입니다. Codelab을 순서대로 진행하는 경우 학습 효과를 극대화할 수 있지만 순서를 바꿔 진행해도 괜찮습니다. 모든 과정 Codelab은 Kotlin Codelab의 고급 Android Codelab 방문 페이지에 나열되어 있습니다.

Google 지도로 앱을 빌드하면 위성 이미지, 지도용 강력한 UI 컨트롤, 위치 추적 및 위치 마커와 같은 기능을 앱에 추가할 수 있습니다. 유명한 낚시터나 등반 구역의 위치와 같은 자신의 데이터 세트를 표시하여 표준 Google 지도에 값을 추가할 수 있습니다. 보물찾기 또는 증강 현실 게임에서와 같이 플레이어가 실제 세계를 탐험하는 게임을 만들 수도 있습니다.

이 강의에서는 맞춤설정된 지도를 표시하고 사용자 위치를 보여주는 Wander라는 Google 지도 앱을 만듭니다.

기본 요건

다음 사항에 대한 지식

  • 기본 Android 앱을 만들고 Android 스튜디오를 사용하여 실행하는 방법을 알아야 합니다.
  • 문자열과 같은 리소스를 만들고 관리하는 방법
  • Android 스튜디오를 사용하여 코드를 리팩터링하고 변수의 이름을 변경하는 방법
  • Google 지도를 사용자로 사용하는 방법
  • 런타임 권한을 설정하는 방법

학습할 내용

  • Google API 콘솔에서 API 키를 가져와 앱에 등록하는 방법
  • 앱에 Google 지도를 통합하는 방법
  • 다른 지도 유형을 표시하는 방법
  • Google 지도의 스타일을 지정하는 방법
  • 지도에 마커를 추가하는 방법
  • 사용자가 관심 장소 (POI)에 마커를 배치하도록 사용 설정하는 방법
  • 위치 추적 사용 설정 방법
  • Google 지도가 삽입된 Wander 앱을 만드는 방법
  • 마커, 스타일 지정 등 앱의 맞춤 기능을 만드는 방법
  • 앱에서 위치 추적을 사용 설정하는 방법

이 Codelab에서는 맞춤 스타일 지정이 포함된 Google 지도를 표시하는 Wander 앱을 만듭니다. Wander 앱을 사용하면 마커를 위치에 드롭하고 오버레이를 추가하며 실시간으로 내 위치를 확인할 수 있습니다.

Android용 Maps SDK에는 API 키가 필요합니다. API 키를 가져오려면 API 및 서비스 페이지에서 프로젝트를 등록하세요. API 키는 앱을 작성자에게 연결하는 디지털 인증서에 연결됩니다. 디지털 인증서 사용 및 앱 서명에 관한 자세한 내용은 앱 서명을 참고하세요.

이 Codelab에서는 디버그 인증서에 API 키를 사용합니다. 디버그 인증서는 디버그 빌드 서명에 설명된 것처럼 설계상 안전하지 않습니다. Android용 Maps SDK를 사용하는 게시된 Android 앱에는 출시 인증서용 키인 두 번째 API 키가 필요합니다. 출시 인증서 가져오기에 대한 자세한 내용은 API 키 가져오기를 참고하세요.

Android 스튜디오에는 유용한 템플릿 코드를 생성하는 Google 지도 활동 템플릿이 포함되어 있습니다. 템플릿 코드에는 API 키 가져오기를 간소화하는 링크가 포함된 google_maps_api.xml 파일이 포함되어 있습니다.

1단계: 지도 템플릿으로 Wander 프로젝트 만들기

  1. 새 Android 스튜디오 프로젝트를 만듭니다.
  2. Google 지도 활동 템플릿을 선택합니다.

  1. 프로젝트 이름을 Wander로 지정합니다.
  2. 최소 API 수준을 API 19로 설정합니다. 언어가 Kotlin인지 확인합니다.
  3. Finish를 클릭합니다.
  4. 앱 빌드가 완료되면 프로젝트와 Android 스튜디오에서 자동으로 생성하는 다음 지도 관련 파일을 살펴봅니다.

google_maps_api.xml: 이 구성 파일을 사용하여 API 키를 보관합니다. 템플릿은 두 개의 google_maps_api.xml 파일을 생성합니다. 하나는 디버그용이고 하나는 출시용입니다. 디버그 인증서의 API 키 파일은 src/debug/res/values에 있습니다. 출시 인증서용 API 키 파일은 src/release/res/values에 있습니다. 이 Codelab에서는 디버그 인증서만 사용합니다.

activity_maps.xml: 이 레이아웃 파일에는 전체 화면에 채워진 단일 프래그먼트가 있습니다. SupportMapFragment 클래스는 Fragment 클래스의 서브클래스입니다. SupportMapFragment은 앱에 지도를 배치하는 가장 간단한 방법입니다. 필요한 수명 주기 요구사항을 자동으로 처리할 수 있도록 지도 뷰 주변의 래퍼입니다.

추가 name 속성과 함께 모든 ViewGroup<fragment> 태그를 사용하여 레이아웃 파일에 SupportMapFragment를 포함할 수 있습니다.

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

MapsActivity.java: MapsActivity.kt 파일은 onCreate() 메서드에서 SupportMapFragment를 인스턴스화하고 class' getMapAsync()를 사용하여 지도 시스템과 뷰를 자동으로 초기화합니다. SupportMapFragment가 포함된 활동은 OnMapReadyCallback 인터페이스 및 인터페이스의 onMapReady() 메서드를 구현해야 합니다. 지도가 로드될 때 onMapReady() 메서드가 호출됩니다.

2단계: API 키 가져오기

  1. google_maps_api.xml 파일의 디버그 버전을 엽니다.
  2. 파일에서 긴 URL이 있는 주석을 찾습니다. URL 매개변수에는 앱에 관한 구체적인 정보가 포함되어 있습니다.
  3. URL을 복사하여 브라우저에 붙여넣습니다.
  4. 안내에 따라 API 및 서비스 페이지에서 프로젝트를 만듭니다. 이 매개변수는 제공된 URL의 매개변수로 인해 Android용 Maps SDK가 자동으로 사용 설정되는지 알 수 있습니다.
  5. API 키 만들기를 클릭합니다.
  6. 다음 페이지에서 API 키 섹션으로 이동하고 방금 만든 키를 클릭합니다.
  7. 키 제한을 클릭하고 Android용 Maps SDK를 선택하여 키의 사용을 Android 앱으로 제한합니다.
  8. 생성된 API 키를 복사합니다. 'AIza"'로 시작합니다.
  9. google_maps_api.xml 파일에서 YOUR_KEY_HERE로 표시된 google_maps_key 문자열에 키를 붙여넣습니다.
  10. 앱을 실행합니다. 활동에 오스트레일리아에 설정된 마커가 있는 삽입된 지도가 표시됩니다. 시드니 마커는 템플릿의 일부이며 나중에 변경합니다.

3단계: mMap 이름 바꾸기

MapsActivity에는 GoogleMap 유형의 mMap라는 비공개 lateinit var이(가) 있습니다. Kotlin 이름 지정 규칙을 따르려면 mMap의 이름을 map로 변경합니다.

  1. MapsActivity에서 mMap를 마우스 오른쪽 버튼으로 클릭하고 Refactor > Rename...

  1. 변수 이름을 map로 변경합니다.

onMapReady() 함수에서 mMap에 대한 모든 참조도 map로 어떻게 변경되는지 확인합니다.

Google 지도에는 일반, 하이브리드, 위성, 지형, '없음'(지도 없음) 등 여러 지도 유형이 포함됩니다.

일반 매핑

위성 지도

하이브리드 지도

지형 지도

각 지도 유형은 서로 다른 종류의 정보를 제공합니다. 예를 들어 지도를 탐색하기 위해 자동차를 이용할 때는 도로명 이름을 확인하면 도움이 되므로 일반 옵션을 사용하면 됩니다. 하이킹을 할 때 지형 지도를 통해 상단에 얼마나 올라가야 하는지 판단할 수 있습니다.

이 작업에서는 다음 작업을 수행합니다.

  1. 사용자가 지도 유형을 변경할 수 있는 옵션 메뉴가 있는 앱 바를 추가합니다.
  2. 지도의 시작 위치를 사용자의 집 위치로 이동합니다.
  3. 지도에 단일 위치를 표시하고 라벨을 포함할 수 있는 마커 지원 기능을 추가합니다.

지도 유형 메뉴 추가

이 단계에서는 사용자가 지도 유형을 변경할 수 있는 옵션 메뉴가 있는 앱 바를 추가합니다.

  1. 새 메뉴 XML 파일을 만들려면 res 디렉터리를 마우스 오른쪽 버튼으로 클릭하고 New > Android Resource File을 선택합니다.
  2. 대화상자에서 파일 이름을 map_options으로 지정합니다.
  3. 리소스 유형으로 메뉴를 선택합니다.
  4. 확인을 클릭합니다.
  5. 코드 탭에서 새 파일의 코드를 다음 코드로 바꿔 지도 메뉴 옵션을 만듭니다. "none" 지도 유형은 아예 지도가 없기 때문에 생략되었습니다. 이 단계에서 오류가 발생하지만 다음 단계에서 이 문제를 해결합니다.
<?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. strings.xml에서 오류를 해결할 수 있도록 title 속성에 리소스를 추가합니다.
<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. MapsActivity에서 onCreateOptionsMenu() 메서드를 재정의하고 map_options 리소스 파일에서 메뉴를 확장합니다.
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
   val inflater = menuInflater
   inflater.inflate(R.menu.map_options, menu)
   return true
}
  1. MapsActivity.kt에서 onOptionsItemSelected() 메서드를 재정의합니다. 사용자의 선택을 반영하도록 지도 유형 상수를 사용하여 지도 유형을 변경합니다.
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. 앱을 실행합니다.
  2. 를 클릭하여 지도 유형을 변경합니다. 지도의 모양이 여러 모드에 따라 어떻게 달라지는지 확인합니다.

기본적으로 onMapReady() 콜백에는 Google 지도가 만들어진 오스트레일리아 시드니에 마커를 배치하는 코드가 포함됩니다. 또한 기본 콜백은 지도를 애니메이션 처리하여 시드니로 이동합니다.

이 작업에서는 지도의 카메라를 집으로 이동하고, 지정한 수준으로 확대/축소하고, 마커를 여기에 배치합니다.

1단계: 집 확대/축소하여 마커 추가하기

  1. MapsActivity.kt 파일에서 onMapReady() 메서드를 찾습니다. 시드니에 마커를 배치하고 카메라를 이동하는 코드를 삭제하세요. 메서드는 다음과 같이 표시됩니다.
override fun onMapReady(googleMap: GoogleMap) {
   map = googleMap

}
  1. 이 안내에 따라 집의 위도와 경도를 찾습니다.
  2. 위도와 경도 값을 만들고 부동 소수점 값을 입력합니다.
val latitude = 37.422160
val longitude = -122.084270
  1. homeLatLng라는 새 LatLng 객체를 만듭니다. homeLatLng 객체에 방금 만든 값을 전달합니다.
val homeLatLng = LatLng(latitude, longitude)
  1. 지도를 확대하고 싶은 정도를 나타내는 val를 만듭니다. 확대/축소 수준 15f를 사용합니다.
val zoomLevel = 15f

확대/축소 수준은 지도의 확대/축소 수준을 제어합니다. 다음 목록을 보면 각 확대/축소 수준이 어느 정도인지 알 수 있습니다.

  • 1: 전 세계
  • 5: 대륙
  • 10: 도시
  • 15: 도로
  • 20: 건물
  1. map 객체에서 moveCamera() 함수를 호출하여 카메라를 homeLatLng로 이동하고 CameraUpdateFactory.newLatLngZoom()를 사용하여 CameraUpdate 객체를 전달합니다. homeLatLng 객체 및 zoomLevel를 전달합니다.
map.moveCamera(CameraUpdateFactory.newLatLngZoom(homeLatLng, zoomLevel))
  1. homeLatLng의 지도에 마커를 추가하세요.
map.addMarker(MarkerOptions().position(homeLatLng))

최종 메서드는 다음과 같습니다.

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. 앱을 실행합니다. 지도가 집으로 이동한 다음 원하는 수준으로 확대되고 집에 마커를 배치해야 합니다.

2단계: 사용자가 긴 마커를 사용하여 마커를 추가하도록 허용

이 단계에서는 사용자가 지도의 위치를 길게 터치하면 마커를 추가합니다.

  1. MapsActivityGoogleMap를 인수로 사용하는 setMapLongClick()라는 메서드 스텁을 만듭니다.
  2. 지도 객체에 setOnMapLongClickListener 리스너를 연결합니다.
private fun setMapLongClick(map:GoogleMap) {
   map.setOnMapLongClickListener { }
}
  1. setOnMapLongClickListener()에서 addMarker() 메서드를 호출합니다. 전달된 LatLng로 설정된 위치를 사용하여 새 MarkerOptions 객체를 전달합니다.
private fun setMapLongClick(map: GoogleMap) {
   map.setOnMapLongClickListener { latLng ->
       map.addMarker(
           MarkerOptions()
               .position(latLng)
       )
   }
}
  1. onMapReady() 메서드의 끝부분에서 map를 사용하여 setMapLongClick()를 호출합니다.
override fun onMapReady(googleMap: GoogleMap) {
   ...
  
   setMapLongClick(map)
}
  1. 앱을 실행합니다.
  2. 특정 위치에 마커를 놓으려면 지도를 길게 터치합니다.
  3. 마커를 탭하면 화면이 중앙에 표시됩니다.

3단계: 마커의 정보 창 추가하기

이 단계에서는 마커를 탭할 때 마커의 좌표를 표시하는 InfoWindow를 추가합니다.

  1. setMapLongClick()setOnMapLongClickListener()에서 snippetval를 만듭니다. 스니펫은 제목 뒤에 표시되는 추가 텍스트입니다. 스니펫에 마커의 위도와 경도가 표시됩니다.
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. addMarker()에서 R.string.dropped_pin 문자열 리소스를 사용하여 마커의 title를 드롭된 핀으로 설정합니다.
  2. 마커의 snippet을(를) snippet(으)로 설정합니다.

완성된 함수는 다음과 같습니다.

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. 앱을 실행합니다.
  2. 위치 아이콘을 드롭하려면 지도를 길게 터치합니다.
  3. 마커를 탭하여 정보 창을 표시합니다.

4단계: 관심 장소 리스너 추가하기

기본적으로 관심 장소 (POI)는 해당 아이콘과 함께 지도에 표시됩니다. 관심 장소에는 공원, 학교, 정부 건물 등이 포함됩니다. 지도 유형이 normal로 설정되면 비즈니스 관심 장소도 지도에 표시됩니다. 비즈니스 관심 장소는 상점, 음식점, 호텔 등의 비즈니스를 나타냅니다.

이 단계에서는 GoogleMap.OnPoiClickListener를 지도에 추가합니다. 이 클릭 리스너는 사용자가 관심 장소를 클릭할 때 지도 위에 즉시 마커를 배치합니다. 또한 클릭 리스너에는 관심 장소 이름을 포함하는 정보 창이 표시됩니다.

  1. MapsActivityGoogleMap를 인수로 사용하는 setPoiClick()라는 메서드 스텁을 만듭니다.
  2. setPoiClick() 메서드에서 전달된 GoogleMapOnPoiClickListener를 설정합니다.
private fun setPoiClick(map: GoogleMap) {
   map.setOnPoiClickListener { poi ->

   }
}
  1. setOnPoiClickListener()에서 마커의 val poiMarker를 만듭니다 .
  2. map.addMarker()MarkerOptions로 사용하여 title를 POI의 이름으로 설정하는 마커로 설정합니다.
private fun setPoiClick(map: GoogleMap) {
   map.setOnPoiClickListener { poi ->
       val poiMarker = map.addMarker(
           MarkerOptions()
               .position(poi.latLng)
               .title(poi.name)
       )
   }
}
  1. setOnPoiClickListener() 함수에서 poiMarker에 대한 showInfoWindow()를 호출하여 정보 창을 즉시 표시합니다.
poiMarker.showInfoWindow()

setPoiClick() 함수의 최종 코드는 다음과 같습니다.

private fun setPoiClick(map: GoogleMap) {
   map.setOnPoiClickListener { poi ->
       val poiMarker = map.addMarker(
           MarkerOptions()
               .position(poi.latLng)
               .title(poi.name)
       )
       poiMarker.showInfoWindow()
   }
}
  1. onMapReady() 끝에서 setPoiClick()를 호출하고 map를 전달합니다.
override fun onMapReady(googleMap: GoogleMap) {
   ...

   setPoiClick(map)
}
  1. 앱을 실행하고 공원이나 커피숍과 같은 관심 장소를 찾습니다.
  2. 관심 장소를 탭하여 관심 장소 마커를 표시하고 정보 창에 관심 장소 이름을 표시합니다.

Google 지도의 독특한 디자인과 분위기를 여러 가지 방법으로 맞춤설정할 수 있습니다.

다른 프래그먼트를 맞춤설정할 때처럼 사용 가능한 XML 속성을 사용하여 MapFragment 객체를 맞춤설정할 수 있습니다. 그러나 이 단계에서는 GoogleMap 객체의 메서드를 사용하여 MapFragment 콘텐츠의 디자인과 분위기를 맞춤설정합니다.

지도에 맞춤설정된 스타일을 만들려면 지도의 지형지물이 표시되는 방법을 지정하는 JSON 파일을 생성합니다. 이 JSON 파일을 수동으로 만들 필요가 없습니다. Google에서는 지도의 스타일을 시각적으로 지정한 후 JSON을 생성하는 지도 플랫폼 스타일 지정 마법사를 제공합니다. 이 작업에서는 레트로 테마로 지도의 스타일을 지정합니다. 즉, 지도에서 빈티지 색상을 사용하고 색상이 있는 도로를 추가합니다.

1단계: 지도 스타일 만들기

  1. 브라우저에서 https://mapstyle.withgoogle.com/으로 이동합니다.
  2. 스타일 만들기를 선택합니다.
  3. Retro를 선택합니다.

  1. 옵션 더보기를 클릭합니다.

  1. 기능 유형 목록에서 로드 > 채우기를 선택합니다.
  2. 도로의 색상을 선택한 색상 (예: 분홍색)으로 변경합니다.

  1. Finish를 클릭합니다.

  1. 결과 대화상자에서 JSON 코드를 복사하고, 원하는 경우 다음 단계에서 사용할 수 있도록 일반 텍스트 메모에 저장합니다.

2단계: 지도에 스타일 추가

  1. Android 스튜디오의 res 디렉터리에서 리소스 디렉터리를 만들고 이름을 raw로 지정합니다. JSON 코드와 같은 raw 디렉터리 리소스를 사용합니다.
  2. res/rawmap_style.json라는 파일을 만듭니다.
  3. 숨긴 JSON 코드를 새 리소스 파일에 붙여넣습니다.
  4. MapsActivity에서 onCreate() 메서드 위에 TAG 클래스 변수를 만듭니다. 기록 목적으로 사용됩니다.
private val TAG = MapsActivity::class.java.simpleName
  1. 또한 MapsActivity에서 GoogleMap를 사용하는 setMapStyle() 함수를 만듭니다.
  2. setMapStyle()에서 try{} 블록을 추가합니다.
  3. try{} 블록에서 스타일 지정에 성공하도록 val success를 만듭니다. 다음 catch 블록을 추가합니다.
  4. try{} 블록에서 맵에 JSON 스타일을 설정하고 GoogleMap 객체의 setMapStyle()를 호출합니다. JSON 파일을 로드하는 MapStyleOptions 객체를 전달합니다.
  5. success에 결과를 할당합니다. setMapStyle() 메서드는 스타일 지정 파일의 파싱과 스타일 설정 성공 상태를 나타내는 부울을 반환합니다.
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. success이 false가 되도록 if 문을 추가합니다. 스타일 지정에 실패하면 파싱에 실패한 로그를 출력합니다.
private fun setMapStyle(map: GoogleMap) {
   try {
       ...
       if (!success) {
           Log.e(TAG, "Style parsing failed.")
       }
   }
}
  1. 누락된 스타일 파일의 상황을 처리하기 위해 catch{} 블록을 추가합니다. catch 블록에서 파일을 로드할 수 없는 경우 Resources.NotFoundException을 발생시킵니다.
private fun setMapStyle(map: GoogleMap) {
   try {
       ...
   } catch (e: Resources.NotFoundException) {
       Log.e(TAG, "Can't find style. Error: ", e)
   }
}

완성된 메서드는 다음 코드 스니펫과 같습니다.

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. 마지막으로, GoogleMap 객체를 전달하는 onMapReady() 메서드에서 setMapStyle() 메서드를 호출합니다.
override fun onMapReady(googleMap: GoogleMap) {
   ...
   setMapStyle(map)
}
  1. 앱을 실행합니다.
  2. 지도를 normal 모드로 설정하면 선택한 스타일의 복고풍 테마와 도로와 함께 새 스타일이 표시됩니다.

3단계: 마커 스타일 지정하기

지도 마커의 스타일을 지정하여 지도를 추가로 맞춤설정할 수 있습니다. 이 단계에서는 기본 빨간색 마커를 좀 더 멋진 것으로 변경합니다.

  1. onMapLongClick() 메서드에서 다음 코드 생성자를 생성자의 MarkerOptions()에 추가하여 기본 마커를 사용하지만 색상을 파란색으로 변경합니다.
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE))

이제 onMapLongClickListener()는 다음과 같습니다.

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. 앱을 실행합니다. 길게 클릭한 후 표시되는 마커가 이제 파란색 음영으로 표시됩니다. onPoiClick() 메서드에 스타일을 추가하지 않았으므로 관심 장소 마커는 계속 빨간색으로 표시됩니다.

Google 지도를 맞춤설정하는 방법 중 하나는 지도 위에 그림을 그리는 것입니다. 이 기법은 인기 있는 낚시 장소와 같은 특정 유형의 위치를 강조하려는 경우에 유용합니다.

  • 도형: 다중선, 다각형, 을 지도에 추가할 수 있습니다.
  • GroundOverlay 객체: 지면 오버레이는 지도에 고정된 이미지입니다. 마커와 달리 지면 오버레이는 화면이 아니라 지표면 방향을 향합니다. 지도를 회전하거나 기울이거나 확대/축소하면 이미지의 방향이 변경됩니다. 지면 오버레이는 단일 이미지를 지도의 한 영역에 고정하려는 경우에 유용합니다.

단계: 지면 오버레이 추가하기

이 작업에서는 홈 위치에 Android 도형의 지면 오버레이를 추가합니다.

  1. 이 Android 이미지를 다운로드하여 res/drawable 폴더에 저장합니다. 파일 이름이 android.png인지 확인하세요.

  1. onMapReady()에서 카메라를 집 위치로 이동하기 위한 호출 후에 GroundOverlayOptions 객체를 만듭니다.
  2. androidOverlay라는 변수에 객체를 할당합니다.
val androidOverlay = GroundOverlayOptions()
  1. BitmapDescriptorFactory.fromResource() 메서드를 사용하여 다운로드한 이미지 리소스에서 BitmapDescriptor 객체를 만듭니다.
  2. 결과 BitmapDescriptor 객체를 GroundOverlayOptions 객체의 image() 메서드에 전달합니다.
val androidOverlay = GroundOverlayOptions()
   .image(BitmapDescriptorFactory.fromResource(R.drawable.android))
  1. 원하는 오버레이의 너비에 관한 float overlaySize를 만듭니다. 이 예에서는 100f의 너비가 적합합니다.

position() 메서드를 호출하여 GroundOverlayOptions 객체의 position 속성을 설정하고 homeLatLng 객체 및 overlaySize를 전달합니다.

val overlaySize = 100f
val androidOverlay = GroundOverlayOptions()
   .image(BitmapDescriptorFactory.fromResource(R.drawable.android))
   .position(homeLatLng, overlaySize)
  1. GoogleMap 객체에서 addGroundOverlay()를 호출하고 GroundOverlayOptions 객체를 전달합니다.
map.addGroundOverlay(androidOverlay)
  1. 앱을 실행합니다.
  2. Android 이미지를 오버레이로 보려면 zoomLevel 값을 18f로 변경합니다.

사용자는 Google 지도를 사용하여 현재 위치를 확인하는 경우가 많습니다. 위치 데이터 레이어를 사용하여 기기 위치를 지도에 표시할 수 있습니다.

위치 데이터 레이어가 내 위치를 지도에 추가합니다. 사용자가 버튼을 탭하면 지도가 기기의 위치를 중심으로 배치됩니다. 기기가 정지해 있으면 위치가 파란색 점으로 표시되고 기기가 움직이면 파란색 갈매기형으로 표시됩니다.

이 작업에서는 위치 데이터 레이어를 사용 설정합니다.

단계: 위치 정보 액세스 권한 요청

Google 지도에서 위치 추적을 사용하려면 코드를 한 줄로 작성해야 합니다. 하지만 사용자가 런타임 권한 모델을 사용하여 위치 정보 액세스 권한을 부여했는지 확인해야 합니다.

이 단계에서는 위치 정보 액세스 권한을 요청하고 위치 추적을 사용 설정합니다.

  1. AndroidManifest.xml 파일에 FINE_LOCATION 권한이 이미 있는지 확인합니다. Google 지도 템플릿을 선택했을 때 Android 스튜디오에서 이 권한을 삽입했습니다.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  1. MapsActivity에서 REQUEST_LOCATION_PERMISSION 클래스 변수를 만듭니다.
private val REQUEST_LOCATION_PERMISSION = 1
  1. 권한이 부여되었는지 확인하려면 MapsActivityisPermissionGranted()라는 메서드를 만듭니다. 이 메서드에서 사용자가 권한을 부여했는지 확인합니다.
private fun isPermissionGranted() : Boolean {
  return ContextCompat.checkSelfPermission(
       this,
      Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
}
  1. 앱에서 위치 추적을 사용 설정하려면 MapsActivityenableMyLocation()이라는 메서드를 만듭니다. 이 메서드는 인수를 사용하지 않고 아무것도 반환하지 않습니다. 내부에서 ACCESS_FINE_LOCATION 권한을 확인합니다. 권한이 부여되면 위치 레이어를 사용 설정합니다. 그 외의 경우에는 권한을 요청합니다.
private fun enableMyLocation() {
   if (isPermissionGranted()) {
       map.isMyLocationEnabled = true 
   }
   else {
       ActivityCompat.requestPermissions(
           this,
           arrayOf<String>(Manifest.permission.ACCESS_FINE_LOCATION),
           REQUEST_LOCATION_PERMISSION
       )
   }
}
  1. onMapReady() 콜백에서 enableMyLocation()를 호출하여 위치 레이어를 사용 설정합니다.
override fun onMapReady(googleMap: GoogleMap) {
   ...
   enableMyLocation()
}
  1. onRequestPermissionsResult() 메서드를 재정의합니다. requestCode REQUEST_LOCATION_PERMISSION 권한이 부여되고 grantResults 배열이 비어 있지 않고 첫 번째 슬롯에서 PackageManager.PERMISSION_GRANTED인 경우 enableMyLocation()를 호출합니다.
override fun onRequestPermissionsResult(
   requestCode: Int,
   permissions: Array<String>,
   grantResults: IntArray) {
   if (requestCode == REQUEST_LOCATION_PERMISSION) {
       if (grantResults.contains(PackageManager.PERMISSION_GRANTED)) {
           enableMyLocation()
       }
   }
}
  1. 앱을 실행합니다. 기기의 위치에 대한 액세스를 요청하는 대화상자가 표시됩니다. 권한을 허용하세요.

이제 지도에서 현재 위치를 파란색 점으로 표시합니다. 위치 버튼이 있습니다. 지도에서 내 위치를 옮기고 이 버튼을 클릭하면 지도가 내 기기의 중심에 다시 놓입니다.

완성된 Codelab의 코드를 다운로드합니다.

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


또는 저장소를 ZIP 파일로 다운로드하여 압축을 푼 후 Android 스튜디오에서 열 수 있습니다.

ZIP 파일 다운로드

  • 지도 API를 사용하려면 Google API 콘솔의 API 키가 필요합니다.
  • Android 스튜디오에서 Google 지도 활동 템플릿을 사용하면 앱의 레이아웃에 단일 SupportMapFragment이 포함된 Activity가 생성됩니다. 또한 템플릿은 앱 매니페스트에 ACCESS_FINE_PERMISSION를 추가하고 활동에 OnMapReadyCallback를 구현하며 필요한 onMapReady() 메서드를 재정의합니다.

런타임 시 GoogleMap의 지도 유형을 변경하려면 GoogleMap.setMapType() 메서드를 사용합니다. Google 지도는 다음과 같은 지도 유형 중 하나일 수 있습니다.

  • 일반: 일반적인 도로 지도입니다. 도로, 인공 지형지물, 주요 자연 지형지물(예: 강)을 표시합니다. 도로 및 지점 라벨도 표시됩니다.
  • 하이브리드: 도로 지도가 추가된 위성 사진 데이터입니다. 도로 및 지점 라벨도 표시됩니다.
  • 위성: 사진 데이터입니다. 도로 및 지형지물 라벨은 표시되지 않습니다.
  • 지형: 지형 데이터가 포함됩니다. 지도에 색상, 등고선, 라벨, 원근감을 나타내는 그림자가 포함됩니다. 도로 및 라벨도 일부 표시됩니다.
  • 없음: 기본 지도 타일이 없습니다.

Google 지도 정보:

  • 마커는 특정 지리적 위치를 나타내는 표시기입니다.
  • 마커를 탭하면 위치 정보가 포함된 정보 창이 표시됩니다.
  • 기본적으로, 관심 지점(POI)은 해당 아이콘과 함께 기본 지도에 나타납니다. 관심 장소에는 공원, 학교, 정부 건물 등이 포함됩니다.
  • 또한 지도 유형이 normal인 경우 비즈니스 관심 장소 (매장, 음식점, 호텔 등)가 기본적으로 지도에 표시됩니다.
  • OnPoiClickListener를 사용하여 관심 장소의 클릭을 캡처할 수 있습니다.
  • 스타일 마법사를 사용하여 거의 모든 Google 지도의 시각적 모양을 변경할 수 있습니다. 스타일 지정 마법사는 setMapStyle() 메서드를 사용하여 Google 지도에 전달할 JSON 파일을 생성합니다.
  • 마커의 기본 색상을 변경하거나 기본 마커 아이콘을 맞춤 이미지로 교체하여 맞춤설정할 수 있습니다.

기타 중요 정보:

  • 지면 오버레이를 사용하여 이미지를 지리적 위치에 고정합니다.
  • GroundOverlayOptions 객체를 사용하여 이미지, 이미지 크기(미터 단위), 이미지 위치를 지정합니다. 이 객체를 GoogleMap.addGroundOverlay() 메서드에 전달하여 오버레이를 지도에 설정합니다.
  • 앱에 ACCESS_FINE_LOCATION 권한이 있는 경우 map.isMyLocationEnabled = true를 설정하여 위치 추적을 사용 설정할 수 있습니다.
  • 이 Codelab에서는 다루지 않지만 특정 위치의 탐색 가능한 파노라마 사진인 Google 스트리트 뷰를 사용하여 위치에 관한 추가 정보를 제공할 수 있습니다.

Android 개발자 문서:

참조 문서

이 과정의 다른 Codelab에 관한 링크는 Kotlin Codelab의 고급 Android Codelab 방문 페이지를 참고하세요.