코틀린(Kotlin)/TIL

[코틀린(Kotlin)] 구글 지도앱 만들기

초보왕보초 2024. 1. 26. 20:33
728x90

1. 지도 사용 설정하기

  • build.gradle의 dependencies 추가
implementation("com.google.android.gms:play-services-maps:18.2.0")
implementation("com.google.android.gms:play-services-location:21.0.1")
  • 퍼미션 추가
<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"/>

 

 

2. 구글 개발자 콘솔에서 지도 API 키 얻기

  • 구글 개발자 콘솔에 접속해 프로젝트를 생성하고 사용자 인증 정보를 만들면 지도 API 키를 발급받을 수 있다
    (무료 사용 하는데도 카드는 등록해야 한다..)
  • 구글 개발자 콘솔에서 얻은 지도 API키를 ManiFest파일에 등록해 준다
  • 레이아웃.xml
<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"/>

 

 

3. 지도 제어하기

  • 지도의 중심 이동하기
    - 지도를 출력하는 뷰 객체를 얻어야 한다
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
    }
}

 

       -  지도의 중심을 이동

val latLng = LatLng(37.566610, 126.978403)
        val position = CameraPosition.Builder()
            .target(latLng)
            .zoom(18f)
            .build()
        googleMap?.moveCamera(CameraUpdateFactory.newCameraPosition(position))

 

       -  마커 표시하기

val markerOptions = MarkerOptions()
        markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_marker))
        markerOptions.position(latLng)
        markerOptions.title("서울시청")
        markerOptions.snippet("Tel:01-120")

        googleMap?.addMarker(markerOptions)

 

       -  위치 요청

val locationRequest = LocationRequest.create().apply {
            interval = 1000
            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 : 지도 화면 변경 이벤트

 

MainActivity.kt

더보기
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)

        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_SHORT).show()
            }
        }

        // 권한 요청
        locationPermission.launch(
            arrayOf(
                android.Manifest.permission.ACCESS_COARSE_LOCATION,
                android.Manifest.permission.ACCESS_FINE_LOCATION
            )
        )
    }

    // 지도 객체를 이용할 수 있는 상황이 될 때
    override fun onMapReady(p0: GoogleMap) {
        val seoul = LatLng(37.566610, 126.978403)
        mGoogleMap = p0
        // default 노말 생략 가능
        mGoogleMap.mapType = GoogleMap.MAP_TYPE_NORMAL
        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 {
            // 업데이트 간격 단위(ms)
            interval = 1000
            // 가장 빠른 업데이트 간격 단위(ms)
            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}")
                        // 계속 실시간으로 위치를 받아오고 있기 때문에 맵을 확대해도 다시 줄어든다
                        setLastLocation(location)
                    }
                }
            }
        }

        // 권한 처리
        if (ActivityCompat.checkSelfPermission(
                this,
                android.Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
                this,
                android.Manifest.permission.ACCESS_COARSE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            return
        }

        // 기기의 위치에 관한 정기 업데이트를 요청하는 메서드 실행
        // 지정한 루퍼 스레드(Looper.myLooper()!!)에서 콜백(locationCallback)으로 위치 업데이트를 요청한다
        fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback,
            Looper.myLooper()!!
        )
    }

    fun setLastLocation(lastLocation: Location) {
        val LATLNG = LatLng(lastLocation.latitude, lastLocation.longitude)

        val markerOptions = MarkerOptions().position(LATLNG).title("현재 위치")
        val cameraPosition = CameraPosition.Builder().target(LATLNG).zoom(15.0f).build()

        mGoogleMap.addMarker(markerOptions)
        mGoogleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
    }
}

 

728x90