위치서비스 등을 사용하여 지도에 위치를 표시하거나 위치정보를 기반으로 좌표나 주소로 변경할 수 있습니다.
준비된 예제는 지도에 위치정보를 수집하여 현재 위치를 표시해주거나 지정한 위치를 지도에 표시해주는 것입니다.
준비사항
API KEY 발급
구글 지도를 사용하기 위해서는 API키 발급이 필요합니다.
아래 링크에서 발급하여 사용할 수 있습니다.
https://developers.google.com/maps/documentation/android/start#get-key
여기에서 STEP3로 바로 넘어가서 사용자 인증 정보 페이지로 이동합니다.
"프로젝트 선택"을 통해 프로젝트를 만들어줍니다.
그리고 API키를 발급받아 가져와줍니다.
Maps프로젝트 생성
신규 프로젝트는 Map이 있는 프로젝트로 생성해줍니다.
그리고 google_maps_api.xml
에 있는 <string name="google_maps_key" translatable="false" templateMergeStrategy="preserve">
태그에 발급받은 API키를 넣어줍니다.
의존성 추가 및 권한 부여
우선 build.gradle
에 다음과 같이 추가해줍니다.implementation 'com.google.android.gms:play-services-location:17.1.0'
그리고 AndroidManifest.xml에 다음과 같이 추가해줍니다.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
MainActivity 작성하기
필요한 변수 선언
lateinit var locationPermission: ActivityResultLauncher<Array<String>>
lateinit var fusedLocationClient:FusedLocationProviderClient
lateinit var locationCallback:LocationCallback
lateinit
은 by lazy
와 같은 것으로 Main
클래스가 생성될 때 초기화되지 않고 변수가 호출(사용)될 때 초기화 되도록 합니다.locationPermission
는 위치정보에 대한 권한 부여를 위해 필요한 변수입니다.fusedLocationClient
는 위치 서비스가 GPS를 이용하여 위치를 확인하기 위해 선언하였습니다. 위에서 gradle
에 추가한 바로 그것입니다.locationCallback
은 위치값 요청에 대한 갱신 정보를 받을 함수입니다.
지금부터 3가지의 커스텀 함수를 작성해주겠습니다.
프로세스 시작
우선 클래스가 OnMapReadyCallback
을 상속받게 해줍니다.
class MapsActivity : AppCompatActivity(), OnMapReadyCallback { ... }
이렇게 추가해주고 아래와 같은 함수를 작성할 것입니다.
fun startProcess() {
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
}
Maps 프로젝트는 뷰 자체가 프래그먼트이기 때문에 프래그먼트 매니저로 제어합니다.
이 때 getMapAsync
는 프래그먼트에서 콜백을 설정할 수 있게 하며 인자로 this
즉 상속받은 OnMapReadyCallback
을 지정해줍니다.
위치 정보를 실시간으로 업데이트 해주는 함수
@SuppressLint("MissingPermission")
fun updateLocation() {
val locationRequest = LocationRequest.create()
locationRequest.run {
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
interval = 1000
}
locationCallback = object : LocationCallback() {
// 1초에 한번씩 변경되는 위치정보가 onLocationResult로 전달됨
override fun onLocationResult(locationResult: LocationResult?) {
locationResult?.let {
for(location in it.locations) {
Log.d("위치정보 - 위도", "${location.latitude}")
Log.d("위치정보 - 경도", "${location.longitude}")
}
}
}
}
// 권한 처리
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper())
}
위치정보는 실시간으로 업데이트되어 계속 화면에 표시되어야 하므로 위와 같은 함수가 필요합니다.
위치정보 요청을 위해 LocationRequest.create()
로 초기화 해줍니다.PRIORITY_HIGH_ACCURACY
는 가장 정확한 위치정보를 반환하게 합니다. priority
에 위치 정보를 넣어주고, interval
을 1000으로 지정하여 1초마다 갱신되게 합니다.
locationCallback
은 LocationCallback
형태의 object
입니다.
1초마다 갱신되는 정보가 onLocationResult
라는 오버라이드한 함수로 전달될 것입니다.
파라미터로 받는 locationResult
에 대해 루프를 돌면서 위도와 경도를 로그로 표시해줍니다.
fusedLocationClient
는 클라이언트의 위치정보를 requestLocationUpdates
로 업데이트 해줍니다.
전달받는 인자는 요청받는 위치정보와 콜백, 그리고 반복가능한 객체가 들어갑니다.
위치를 설정하는 함수
updateLocation
함수를 통해 위치정보를 갱신하였다면 이제 setLastLocation
이라는 함수로 위치 정보를 지도에 fix
해 줄 것입니다.
fun setLastLocation(lastLocation:Location) {
val latlng = LatLng(lastLocation.latitude, lastLocation.longitude)
// 위도와 경도에 대한 옵션
val makerOptions = MarkerOptions().position(latlng).title("I'm Here!")
// 카메라 옵션
val cameraPosition = CameraPosition.Builder().target(latlng).zoom(15.0f).build()
mMap.clear()
mMap.addMarker(makerOptions)
mMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
}
LatLng
를 통해 위도와 경도를 받아 저장합니다.MarkerOptions
을 통해 수집된 위치에 핀 포인트를 꽂아줄 것입니다. 핀을 클릭하면 "I'm Here!"라는 메시지를 보여줄 것입니다.
이 때 카메라 옵션을 지정해주는데 이는 카메라가 가리키는 방향을 북쪽에서 시계방향으로 설정해줍니다.
초기화, 마커추가 및 카메라 움직임에 따른 지도 표현을 활성화 해줍니다.
이 함수를 updateLocation
함수 내에 있는 onLocationResult
에 사용합니다.
@SuppressLint("MissingPermission")
fun updateLocation() {
val locationRequest = LocationRequest.create()
locationRequest.run {
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
interval = 1000
}
locationCallback = object : LocationCallback() {
// 1초에 한번씩 변경되는 위치정보가 onLocationResult로 전달됨
override fun onLocationResult(locationResult: LocationResult?) {
locationResult?.let {
for(location in it.locations) {
Log.d("위치정보 - 위도", "${location.latitude}")
Log.d("위치정보 - 경도", "${location.longitude}")
setLastLocation(location)
}
}
}
}
// 권한 처리
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper())
}
onMapReady
함수 구성하기
이 함수는 오버라이드 된 함수이며 다음과 같이 구성합니다.
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
updateLocation()
}
(부록) 본인이 설정한 위도, 경도로 적용하는 경우
// 본인이 설정한 위도, 경도로 적용하는 경우
fun setLocation(latitude:Double, longitude:Double) {
val latlng = LatLng(latitude, longitude)
// 위도와 경도에 대한 옵션
val makerOptions = MarkerOptions().position(latlng).title("I'm Here!")
// 카메라 옵션
val cameraPosition = CameraPosition.Builder().target(latlng).zoom(15.0f).build()
mMap.clear()
mMap.addMarker(makerOptions)
mMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
}
위도와 경도를 함수의 매개변수로 받습니다.
onCreate
구성
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMapsBinding.inflate(layoutInflater)
setContentView(binding.root)
locationPermission = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { results ->
if(results.all { it.value }) {
startProcess()
} else { // 문제 발생 시
Toast.makeText(this, "권한 승인이 필요합니다.", Toast.LENGTH_LONG).show()
}
}
// 권한 요청
locationPermission.launch(
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
}
locationPermission
은 ActivityResultLauncher<Array<String>>
로 선언된 배열 형태입니다.
안드로이드 개발자 문서를 보면
결과를 얻기 위해 활동을 시작할 때, 메모리 부족으로 인해 프로세스와 활동이 소멸될 수 있습니다(특히, 카메라 사용과 같이 메모리를 많이 사용하는 작업의 경우 소멸 확률이 매우 높음).
따라서, Activity Result API는 다른 활동을 실행하는 코드 위치에서 결과 콜백을 분리합니다. 결과 콜백은 프로세스와 활동을 다시 생성할 때 사용할 수 있어야 하므로 다른 활동을 실행하는 로직이 사용자 입력 또는 기타 비즈니스 로직을 기반으로만 발생하더라도 활동이 생성될 때마다 콜백을 무조건 등록해야 합니다.
라고 되어있으며 추가적으로
ComponentActivity
또는Fragment
에 있을 때, Activity Result API에서 제공하는registerForActivityResult()
API를 통해 결과 콜백을 등록할 수 있습니다.registerForActivityResult()
는ActivityResultContract
및ActivityResultCallback
을 가져와서 다른 활동을 실행하는 데 사용할ActivityResultLauncher
를 반환합니다.
ActivityResultContracts.RequestMultiplePermissions()
는 런타임 요청을 요청하는 역할을 하며 사용자의 응답을 처리하는 콜백입니다.
권한 획득에 성공하였을 경우 startProcess
메서드를 호출하여 실행하고, 문제가 발생했을 때 토스트를 띄워줍니다.
아래 locationPermission.launch
는 권한을 호출하는 함수입니다.
런타임 시 위치 정보 액세스 권한 요청 시 사용자 선택이 권한 부여에 영향을 미치는데, 아래 표는 사용자가 권한 런타임 대화상자에서 선택하는 옵션에 따라 시스템이 앱에 부여하는 권한을 보여줍니다.
정확한 위치 | 대략적인 위치 | |
---|---|---|
앱 사용 중에만 허용 | ACCESS_FINE_LOCATION 및 ACCESS_COARSE_LOCATION |
ACCESS_COARSE_LOCATION |
이번만 허용 | ACCESS_FINE_LOCATION 및 ACCESS_COARSE_LOCATION |
ACCESS_COARSE_LOCATION |
거부 | 위치 정보 액세스 권한 없음 | 위치 정보 액세스 권한 없음 |
'Study > Android' 카테고리의 다른 글
[Android] 블루투스 개념 (2) | 2022.07.17 |
---|---|
[Android] Android 12에서 강화된 블루투스 퍼미션 (0) | 2022.05.20 |
Android : Camera (0) | 2022.02.16 |
Android : SQLite (0) | 2022.02.16 |
Android : 장면 전환 시 데이터 주고 받기 (0) | 2022.02.16 |