-1- GoogleMaps를 통해 MapView 만들기 - Activity에서 띄우기
- 1. GoogleMap API키 받아오기
- 2. build.gradle에 dependencies 추가
- 3. Manifest에 ① 에서 받아온 API Key넣어주기, 권한 받아오기( 사용자에게 직접 권한요청을 해도 된다 )
- 4. xml에서 MapView 생성하기.
- 5. MainActivity에서 GoogleMap 만들기
GoogleMap API Key생성하기
- 화면 좌측 라이브러리 탭으로 이동
- Maps SDK for Android 선택
- 사용 클릭
- 사용자 인증 정보 만들기 클릭 후 API 키 항목 선택 후 진행
결제 정보 입력하라는 내용 따라가서 진행하면 된다. 무료체험이 끝나도 별도로 결제의사가 없으면 자동결제 X
- 키 및 사용자 인증 정보에서 API키 확인
Google Play services 라이브러리 설치하기
- Android Studio 상단 메뉴바 Tools → SDK Manager → SDK Tools 로 들어가 Google Play services 설치
build.gradle 추가하기
- 버전 확인 후 최신버전으로 추가해주기
dependencies {
implementation("com.google.android.gms:play-services-location:21.0.1")
implementation("com.google.android.gms:play-services-maps:18.1.0")
...
}
Manifest에 meta-data 로 API 키 추가 및 권한 추가.
<manifest>
...
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
// meta-data는 <application> 안에 넣어주면 된다 </application>
<application
...
>
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="API 키" />
...
</application>
</manifest>
- meta-data의 API키를 넣어줄 때 name의 geo는 geolocation 의 뜻하며 지리적 위치 또는 위치 기반 서비스와 관련된 것임을 나타낸다. Google Maps API를 사용하려면 지도 서비스 및 위치 관련 데이터에 접근하기 위한 API키가 필요하며, 이 API 키를 설정하는 meta-data 요소의 이름이 "com.google.android.geo.API_KEY" 로 공식적으로 지정되어있다.
acitivity_main.xml
- MapView 생성해주기
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.gms.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt
- 1. xml에 쉽게 접근하기 위해서 ViewBinding 적용 및 GoogleMap, MapView 지연초기화로 선언 해주고 MapView의 생명주기는 Activity의 생명주기에 맞게 작성해준다
- 자원관리 및 성능 최적화와 데이터 관리, 보안 및 데이터 무결성을 위해서도 MapView의 생명주기를 Activity의 생명주기와 맞춰주어야 한다.
- 2. Activity에서 OnMapReadyCallBack 인터페이스를 상속 받아준다.
class MainActivity : AppCompatActivity(),OnMapReadyCallback {
private lateinit var binding: ActivityMainBinding
private lateinit var mapView: MapView
private lateinit var mMap : GoogleMap
private var currentMarker: Marker? =null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
this.mapView = binding.mapView
mapView.onCreate(savedInstanceState)
mapView.getMapAsync(this)
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onResume() {
super.onResume()
mapView.onResume()
}
override fun onPause() {
super.onPause()
mapView.onPause()
}
override fun onLowMemory() {
super.onLowMemory()
mapView.onLowMemory()
}
override fun onDestroy() {
mapView.onDestroy()
super.onDestroy()
}
3. ActivityResultLauncher<Array<String>>을 통해 권한 확인 및 요청하기.
- 3 - 1 import android.Manifest - Import 해주기
import android.Manifest
-
- 3 - 2 권한 확인 및 권한 요청하기
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// 권한 요청
locationPermission = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) {results ->
if (!results.all { it.value }){
Toast.makeText(this,"권한 승인이 필요합니다.",Toast.LENGTH_SHORT).show()
}
}
locationPermission.launch(
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
...
}
}
4. setLocation 함수 만들기, onMapReady 메소드 만들기
override fun onMapReady(googleMap: GoogleMap) {
googleMap.uiSettings.apply {
isZoomControlsEnabled
isZoomGesturesEnabled
isRotateGesturesEnabled = false
}
mMap = googleMap
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
setLocation(37.602589, 127.143674)!!.showInfoWindow()
}
private fun setLocation(latitude: Double, longitude: Double): Marker? {
val positionLatLng = LatLng(latitude!!, longitude!!)
val markerOption = MarkerOptions().apply {
position(positionLatLng)
title("Here!")
snippet("기본")
}
val cameraOption = CameraPosition.builder()
.target(positionLatLng)
.zoom(15f)
.build()
mMap.mapType = GoogleMap.MAP_TYPE_NORMAL
mMap.animateCamera(CameraUpdateFactory.zoomTo(15f))
mMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraOption))
return mMap.addMarker(markerOption)
}
- 4 - 1 setLocation 함수 만들기
- markerOption, Camera Position 설정하기.
- MarkerOption
- latitude와 longitude 값을 받아 해당 위치에 마커를 표시하고, 마커에 title, snipper을 통해 사용자에게 정보 전달을 한다. title 해당 위치 마커의 제목으로 사용되고, snippet은 부연설명을 제공한다.
- CameraPosition
- Camera( Google Map에서 보여지는 화면 )의 위치 및 zoom 레벨
- zoom 레벨 - 0 부터 시작하여 더 큰 값을 가질 수록 지도가 더 확대 된다.
- MarkerOption
- mapType 지정
- MAP_TYPE_NORMAL
- 일반 도로 지도 - 도로 등의 중요한 특징들이 표시되는 지도
- MAP_TYPE_HYBRID
- 도로 지도와 위성사진 데이터가 동시에 표시되는 지도
- MAP_TYPE_SATELLITE
- 위성사진 데이터로 도로 및 특징이 표시되 지도 (대한민국은 위성지도 사용제한이라고 한다.)
- MAP_TYPE_TERRAIN
- 지형도 데이터로 색상, 등고선 및 원근 음영이 표시되는 지도
- addMarker(markerOption)
- markerOption 에서 설정한 Option으로 화면에 표시될 Marker 추가
- MAP_TYPE_NORMAL
- markerOption, Camera Position 설정하기.
- 4 - 2 onMapReady 매소드 생성
- GoogleMap.uiSettings - 지도의 화면 확대, 화면 회전등의 유저와 GoogleMap UI와 상호작용 및 화면 UI 설정을 조정하는 인터페이스 이다.
-
- isRotateGesturesEnabled : Boolean
- 지도 회전 제스처를 사용할 수 있는지 여부를 설정한다.
- isZoomControlsEnabled : Boolean
- 지도 화면에 확대 / 축소 컨트롤을 표시할지 여부를 설정한다.
- isZoomGesturesEnabled : Boolean
- 확대 / 축소 제스처(핀치 줌)를 사용할 수 있는지 여부를 설정한다.
- isCompassEnabled : Boolean
- 지도 화면에 나침반 컨트롤을 표시할 지 여부를 설정한다.
- isMyLocationButtonEnabled : Boolean
- 현재 위치 버튼을 표시할지 여부를 설정한다. 해당 버튼을 누르면 지도가 현재 위치로 이동한다
- 현재 위치 권환 허용을 필요로 한다.
- isMapToolbarEnabled : Boolean
- 지도 도구 모음을 할성화할지 여부를 설정한다. 도구 모음에는 마커 및 지도 정보 공유, 지도 및 거리 뷰 전환 등의 옵션이 포함된다.uiSettings 기능
- isZoomControlsEnabled : Boolean
- 지도 화면에 확대 / 축소 컨트롤을 표시할지 여부를 설정한다
- isRotateGesturesEnabled : Boolean
-
- getFusedLocationProviderClient(this)
- FusedLocationProviderClient 객체를 얻기 위한 팩토리 메서드로 주어진 this(activity) Context를 기반으로 FusedLocationProviderClien를 생성한다.
- onMapReady 메서드에서 FusedLocationProviderClient를 초기화 하는 이유는 지도를 표시하고 나면 사용자의 현재 위치를 추적하거나 지도의 초기 위치를 설정하는 것이 일반적이기 때문이다.
- showInfoWindow()
- Google Maps 화면이 시작되었을 때 마커와 함께 지정해놓은 title, snipper 을 보여준다. showInfoWindow()를 사용하지 않을 경우 마커의 위치는 확인되지만 title, snippet은 마커를 클릭해야 확인이 가능하다.
- GoogleMap.uiSettings - 지도의 화면 확대, 화면 회전등의 유저와 GoogleMap UI와 상호작용 및 화면 UI 설정을 조정하는 인터페이스 이다.
완성코드 MainAcitivty.kt
class MainActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var binding: ActivityMainBinding
private lateinit var mapView: MapView
private lateinit var mMap: GoogleMap
lateinit var locationPermission: ActivityResultLauncher<Array<String>>
lateinit var fusedLocationProviderClient: FusedLocationProviderClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// 권한 요청
locationPermission = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) {results ->
if (!results.all { it.value }){
Toast.makeText(this,"권한 승인이 필요합니다.",Toast.LENGTH_SHORT).show()
}
}
locationPermission.launch(
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
// Activity에서 GoogleMaps를 사용하는 경우
mapView = binding.mapView
mapView.onCreate(savedInstanceState)
mapView.getMapAsync(this)
}
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
mMap.uiSettings.apply {
isZoomControlsEnabled
isZoomGesturesEnabled
isRotateGesturesEnabled = false
isMapToolbarEnabled = true
}
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
setLocation(37.602589, 127.143674)!!.showInfoWindow()
}
private fun setLocation(latitude: Double, longitude: Double): Marker? {
val positionLatLng = LatLng(latitude!!, longitude!!)
val markerOption = MarkerOptions().apply {
position(positionLatLng)
title("Here!")
snippet("기본")
}
val cameraOption = CameraPosition.builder()
.target(positionLatLng)
.zoom(15f)
.build()
mMap.mapType = GoogleMap.MAP_TYPE_NORMAL
mMap.animateCamera(CameraUpdateFactory.zoomTo(15f))
mMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraOption))
return mMap.addMarker(markerOption)
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onResume() {
super.onResume()
mapView.onResume()
}
override fun onPause() {
super.onPause()
mapView.onPause()
}
override fun onLowMemory() {
super.onLowMemory()
mapView.onLowMemory()
}
override fun onDestroy() {
mapView.onDestroy()
super.onDestroy()
}
}
작동화면
2. 현재 위치 가져오기
MainActivity.kt
- 1. 에서 작성한 MainActivity.kt에서 setLocation을 updateLocation(), setLastLocation()으로 변경
- updateLocation()의 LocationRequest.create() 가 Deprecated 되었기 때문에 Location.Builder로 변경되었다.
class MainActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var binding: ActivityMainBinding
private lateinit var mapView: MapView
private lateinit var mMap: GoogleMap
lateinit var locationPermission: ActivityResultLauncher<Array<String>>
lateinit var fusedLocationProviderClient: FusedLocationProviderClient
lateinit var locationCallback: LocationCallback
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// 권한 요청
locationPermission = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { results ->
if (!results.all { it.value }) {
Toast.makeText(this, "권한 승인이 필요합니다.", Toast.LENGTH_SHORT).show()
}
}
locationPermission.launch(
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
// Activity에서 GoogleMaps를 사용하는 경우
mapView = binding.mapView
mapView.onCreate(savedInstanceState)
mapView.getMapAsync(this)
}
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
mMap.uiSettings.apply {
isZoomControlsEnabled
isZoomGesturesEnabled
isRotateGesturesEnabled = false
isMapToolbarEnabled = true
}
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
// setLocation(37.602589, 127.143674)!!.showInfoWindow()
updateLocation()
}
fun updateLocation() {
val locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY,5000).apply {
setMinUpdateDistanceMeters(0f)
setGranularity(Granularity.GRANULARITY_PERMISSION_LEVEL)
setWaitForAccurateLocation(true)
}.build()
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
locationResult?.let {
for (location in it.locations) {
setLastLocation(location)
}
}
}
}
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
return
}
fusedLocationProviderClient.requestLocationUpdates(
locationRequest, locationCallback,
Looper.myLooper()!!
)
}
fun setLastLocation(lastLocation: Location) {
val LATLNG = LatLng(lastLocation.latitude, lastLocation.longitude)
val makerOptions = MarkerOptions().apply {
position(LATLNG)
title("Here")
snippet("기본")
}
val cameraOption = CameraPosition.builder()
.target(LATLNG)
.zoom(15f)
.build()
mMap.addMarker(makerOptions)
mMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraOption))
}
// fun setLocation(latitude: Double, longitude: Double): Marker? {
// val positionLatLng = LatLng(latitude!!, longitude!!)
// val markerOption = MarkerOptions().apply {
// position(positionLatLng)
// title("Here!")
// snippet("기본")
// }
//
// val cameraOption = CameraPosition.builder()
// .target(positionLatLng)
// .zoom(15f)
// .build()
//
// mMap.mapType = GoogleMap.MAP_TYPE_NORMAL
// mMap.animateCamera(CameraUpdateFactory.zoomTo(15f))
// mMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraOption))
// return mMap.addMarker(markerOption)
// }
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onResume() {
super.onResume()
mapView.onResume()
}
override fun onPause() {
super.onPause()
mapView.onPause()
}
override fun onLowMemory() {
super.onLowMemory()
mapView.onLowMemory()
}
override fun onDestroy() {
mapView.onDestroy()
super.onDestroy()
}
}
[오늘 복습한 내용]
1. Google Map을 이해하려고 며칠동안 잡고 복습하고 있다.
[오류,에러 등등]
1. Manifest에서 Permission을 추가해줬는데 해당 부분이 오류가 남.
해결방법 → 직접 코드 작성으로 Manifest Import 해주기
import android.Manifest
2. Resource$NotFoundException 오류
FATAL EXCEPTION: main Process: com.example.googlemaps, PID: 6468 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.googlemaps/com.example.googlemaps.MainActivity}: android.content.res.Resources$NotFoundException: Resource ID #0x7f070016 type #0x3 is not valid
잘 돌아가던 앱이 갑자기 실행이 안되어서 앱 삭제 후 다시 설치하니까 정상작동 되더라
[느낀 점]
1. 잠을 줄여야겠다
2. 조금 더 열심히 해야겠다
3. 시간이 너무 빠르다
[Reference]
// GoogleMaps
https://eunoia3jy.tistory.com/185
'TIL' 카테고리의 다른 글
[TIL] 공공 데이터 API 받아오기 / Retrofit2, OkHttp3 를 사용한 간단한 앱 (0) | 2023.09.19 |
---|---|
[TIL] Kotlin GoogleMaps 만들기 - 2. Fragment에서 띄우기 / Geocoder (0) | 2023.09.18 |
[TIL] Kotlin 이미지 로딩 라이브러리 Glide, Coil (0) | 2023.09.15 |
[Error] RecyclerView ImageView에 파란Image가 불러와지는 오류 (0) | 2023.09.15 |
[TIL] Kotlin DataBinding/ DataBinding과 ViewBinding과 차이 (0) | 2023.09.14 |