2024. 7. 31. 15:03ㆍ[개발]/Kotlin 활용 앱 개발
# 지도 사용 설정
1) gradle에 추가할 코드
// dependencies
implementation 'com.google.android.gms:play-services-maps:18.1.0'
implementation 'com.google.android.gms:play-services-location:21.0.1'
2) manifest.xml에 추가할 코드
// permissions
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET"/>
// 구글맵 API 키 등록
<uses-library android:name="org.apache.http.legacy" android:required="true"/>
<meta-data android:name="com.google.android.maps.v2.API_KEY"
android:value="### 구글 지도 API 키 등록 ###"/>
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>
# 구글 개발자 콘솔에서 지도 API 키 발급
- https://console.cloud.google.com/?pli=1 에서 프로젝트 생성, 사용자 인증 정보 만들고 API 키 발급
- 발급받은 키를 manifest 파일에 등록
# 레이아웃 설정
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.google.android.gms.maps.SupportMapFragment"/>
# 지도 출력 뷰 객체 생성
class MainActivity : AppCompatActivity(), OnMapReadyCallback {
var googleMap: GoogleMap? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
(supportFragmentManager.findFragmentById(R.id.mapView) as SupportMapFragment)!!.getMapAsync(this)
// 지도 객체를 이용할 수 있는 상황이 될 때
override fun onMapReady(p0: GoogleMap?) {
googleMap = p0
}
}
# 지도 제어
1) 지도 중심 이동
// 위도, 경도 표시
val latLng = LatLng(37.566610, 126.978403)
val position = CameraPosition.Builder()
.target(latLng)
.zoom(18f)
.build()
googleMap?.moveCamera(CameraUpdateFactory.newCameraPosition(position))
2) 위치에 마커 표시
val markerOptions = MarkerOptions()
markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_marker))
markerOptions.position(latLng)
markerOptions.title("서울시청")
// 작은 설명 표시 가능
markerOptions.snippet("Tel:01-120")
googleMap?.addMarker(markerOptions)
3) 위치 요청
val locationRequest = LocationRequest.create().apply {
// 1000밀리초 = 1초마다 업데이트
interval = 1000
// 500밀리초 = 0.5초 빠른 업데이트
fastestInterval = 500
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
locationCallback = object : LocationCallback(){
//1초에 한번씩 변경된 위치 정보가 onLocationResult 으로 전달된다.
override fun onLocationResult(locationResult: LocationResult) {
locationResult?.let{
for (location in it.locations){
Log.d("위치정보", "위도: ${location.latitude} 경도: ${location.longitude}")
}
}
}
}
# 지도에서 사용자 이벤트 처리
- GoogleMap.OnMapClickListener: 지도 클릭 이벤트
- GoogleMap.OnMapLongClickListener: 지도 롱 클릭 이벤트
- GoogleMap.OnMarkerClickListener: 마커 클릭 이벤트
- GoogleMap.OnMarkerDragListener: 마커 드래그 이벤트
- GoogleMap.OnInfoWindowClickListener: 정보 창 클릭 이벤트
- GoogleMap.OnCameraIdleListener: 지도 화면 변경 이벤트
googleMap?.setOnMapClickListener { latLng ->
Log.d("map_test", "click : ${latLng.latitude} , ${latLng.longitude}")
}
googleMap?.setOnMapLongClickListener { latLng ->
Log.d("map_test", "long click : ${latLng.latitude} , ${latLng.longitude}")
}
googleMap?.setOnCameraIdleListener {
val position = googleMap!!.cameraPosition
val zoom = position.zoom
val latitude = position.target.latitude
val longitude = position.target.longitude
Log.d("map_test", "User change : $zoom $latitude , $longitude")
}
googleMap?.setOnMarkerClickListener { marker ->
true
}
googleMap?.setOnInfoWindowClickListener { marker ->
}
# 예제
1) gradle 파일에 googlemap library 추가
dependencies {
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
// 여기 두줄 추가!
implementation 'com.google.android.gms:play-services-maps:18.1.0'
implementation 'com.google.android.gms:play-services-location:21.0.1'
}
2) manifest.xml 파일에 permission, 구글맵 api 추가
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
// permission 입력
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.MapTest"
tools:targetApi="31">
// API 키 입력
<uses-library android:name="org.apache.http.legacy" android:required="true"/>
<meta-data android:name="com.google.android.maps.v2.API_KEY"
android:value="== Google Cloud에서 생성한 API키 입력!! ==="/>
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
3) 레이아웃 설정
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.google.android.gms.maps.SupportMapFragment"/>
4) MainActivity 코딩
package com.android.ex11_googlemap
import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.location.Location
import android.os.Bundle
import android.os.Looper
import android.util.Log
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationListener
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.BitmapDescriptorFactory
import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions
import com.google.android.gms.tasks.OnSuccessListener
// OnMapReady를 같이 상속
class MainActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var mGoogleMap: GoogleMap
//위치 서비스가 gps를 사용해서 위치를 확인
lateinit var fusedLocationClient: FusedLocationProviderClient
//위치 값 요청에 대한 갱신 정보를 받는 변수
lateinit var locationCallback: LocationCallback
lateinit var locationPermission: ActivityResultLauncher<Array<String>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Permission을 갖고 있는지 확인
locationPermission = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()){ results ->
if(results.all{it.value}){
(supportFragmentManager.findFragmentById(R.id.mapView) as SupportMapFragment)!!.getMapAsync(this)
}else{ //문제가 발생했을 때
Toast.makeText(this,"권한 승인이 필요합니다.",Toast.LENGTH_LONG).show()
}
}
//권한 요청
locationPermission.launch(
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
}
// OnMapReady를 상속받음으로써 구현해야 하는 메서드
// 지도 객체를 이용할 수 있는 상황이 될 때 자동 콜백
override fun onMapReady(p0: GoogleMap) {
val seoul = LatLng(37.566610, 126.978403)
mGoogleMap = p0
mGoogleMap.mapType = GoogleMap.MAP_TYPE_NORMAL // default 노말 생략 가능
mGoogleMap.apply {
val markerOptions = MarkerOptions()
markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE))
markerOptions.position(seoul)
markerOptions.title("서울시청")
markerOptions.snippet("Tel:01-120")
addMarker(markerOptions)
}
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
updateLocation()
}
fun updateLocation(){
val locationRequest = LocationRequest.create().apply {
interval = 1000
fastestInterval = 500
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
locationCallback = object : LocationCallback(){
//1초에 한번씩 변경된 위치 정보가 onLocationResult 으로 전달된다.
// 1초에 한번씩 위치 정보라 로그로 찍힘
override fun onLocationResult(locationResult: LocationResult) {
locationResult?.let{
for (location in it.locations){
Log.d("위치정보", "위도: ${location.latitude} 경도: ${location.longitude}")
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
}
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback,
Looper.myLooper()!!
)
}
fun setLastLocation(lastLocation: Location){
val LATLNG = LatLng(lastLocation.latitude,lastLocation.longitude)
val makerOptions = MarkerOptions().position(LATLNG).title("나 여기 있어용~")
val cameraPosition = CameraPosition.Builder().target(LATLNG).zoom(15.0f).build()
mGoogleMap.addMarker(makerOptions)
mGoogleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
}
}
5) 실행 결과
- Emulator는 가상의 화면이므로 현재 위치가 변경됨에 따라 코드가 잘 작동하는지 확인할 수 없음
- 가상의 위치를 설정하여 검증 가능
① 에뮬레이터 컨트롤(에뮬레이터 위에 점3개) 클릭
② Location → 가상의 위치 검색 → set Location 클릭
③ 가상의 위치가 현재 위치로 설정됨
'[개발] > Kotlin 활용 앱 개발' 카테고리의 다른 글
Retrofit 실습 예제 (1) 시도별 미세먼지 현황 앱 (0) | 2024.07.31 |
---|---|
Retrofit 개념 (0) | 2024.07.31 |
사용자 위치 얻기 (0) | 2024.07.31 |
데이터 저장 - Room (0) | 2024.07.31 |
데이터 저장 - SharePreferences (0) | 2024.07.31 |