본문 바로가기
안드로이드 코틀린

[Kotlin][Android] 구글맵 빠르게 적용하기

by teamnova 2021. 7. 12.

안녕하세요. 이번에는 코틀린을 통하여 구글 지도를 제 앱에 띄우는 것을 해보도록 하겠습니다. 

이를 구현하기 위해서는 구글 맵 API를 사용해야 하는데요. API(Application Programing Interface)가 무엇인지 간단하게 설명드리면, 응용 프로그램(일반적인 앱을 생각하시면 됩니다.)에서 다른 프로그램(운영체제, 프로그램 언어, 서버, 데이터 베이스 등등)에서 제공하는 기능을 제어할 수 있게 만든 인터페이스(매개체) 입니다.

 

구글 맵 API를 사용하려면 구글 플레이 서비스 SDK를 설치해야 합니다. 

상단 메뉴의 [Tools] - [SDK Manager]를 클릭해 줍시다.

클릭하게 되면 아래 사진처럼 SDK 설정 화면이 나옵니다. [SDK Tools] 탭을 클릭하면 안드로이드 개발에 필요한 SDK를 설치 할 수 있습니다. 저희는 필요한게 Google Play services이므로 체크박스에 체크 후 OK 버튼을 클릭해줍니다.

다음과 같은 확인 창이 나타나면 OK 버튼을 클릭해줍시다. 

 

이번에는 구글 맵 Activity를 사용하겠습니다. 파일 이름은 GoogleMapsActivity으로 하겠습니다.

 

구글 플레이 서비스에 접근 하기위해서는 API 키가 필요합니다.

 

[app]-[res]-[values] 디렉터리에 있는 google_maps_api.xml 에서 'https://' 로 시작하는 첫 번째 URL을 복사해 웹 브라우저의 주소창에 붙어넣고 이동합니다.

그러면 다음과 같이 Google Cloud Platform 페이지 열렸을 겁니다. 계속 버튼을 클릭해줍니다.

자동으로 프로젝트가 생성되고 API 사용 설정 화면으로 이동하게 됩니다. API를 호출하기 위해서 [API 키 만들기] 버튼을 클릭해줍시다.

정상적으로 진행 되면 아래와 같이 보이게 됩니다. 에뮬레이터로 테스트 하실 분은 연필모양을 클릭해서 제한사항이 현재 'Android 앱'으로 되어 있는 것을 없음으로 표시해 줍시다.

그리고 발급 받은 키를 구글 google_maps_api.xml에서 '.YOUR_KEY_HERE' 부분에다 붙여 넣어 줍시다.

그후 앱을 빌드해보면 아래와 같이 오스트레일리아에 마커가 표시된 지도가 보이게 됩니다.

자기 자신의 위치를 표시하기 위해서 AndroidManifest.xml에 위치 조회에 대한 권한을 등록해줘야 합니다. 이때까지 쭉 따라오셨다면 이 작업은 따로 해주실 필요가 없습니다. 왜냐하며 Google Maps Activity로 프로젝트를 생성했다면 자동으로 아래 권한 코드가 추가되기 때문입니다.

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

또한 구글에서는 현재 위치를 검색하는 FusedLocationProviderClient API를 제공하는데요. 이는 GPS 신호 및 와이파이와 통신사 네트워크 위치를 결합해서 최소한의 배터리 사용량으로 빠르고 정확하게 위치를 검색해줍니다. 

FusedLocationProviderClient API를 사용하기 위해서 build.gradle 파일에 구글 플레이 서비스의 Location 라이브러 의존성을 추가합니다. 이때 주의할 점이 Location 라이브러리는 Maps 라이브러리와 버전이 같아야 한다는 겁니다.

 

dependencies {
   // 다른 라이브러리 들
    
    // 구글 맵
    implementation 'com.google.android.gms:play-services-location:17.0.0'
    implementation 'com.google.android.gms:play-services-maps:17.0.0'

}

 

GoogleMapsActivity 관련된 코드 입니다.

class GoogleMapsActivity : AppCompatActivity(), OnMapReadyCallback {

    private lateinit var mMap: GoogleMap
    // 현재 위치를 검색하기 위함
    private lateinit var fusedLocationClient: FusedLocationProviderClient // 위칫값 사용
    private lateinit var locationCallback: LocationCallback // 위칫값 요청에 대한 갱신 정보를 받아옴

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_google_maps)

        // 사용할 권한 array로 저장
        val permissions = arrayOf(
            Manifest.permission.ACCESS_COARSE_LOCATION,
            Manifest.permission.ACCESS_FINE_LOCATION)

        requirePermissions(permissions, 999) //권환 요쳥, 999는 임의의 숫자
    }

    fun startProcess() {
        // SupportMapFragment를 가져와서 지도가 준비되면 알림을 받습니다.
        val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
        mapFragment.getMapAsync(this)
    }

    /** 권한 요청*/
    fun requirePermissions(permissions: Array<String>, requestCode: Int) {
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            permissionGranted(requestCode)
        } else {
            val isAllPermissionsGranted = permissions.all { checkSelfPermission(it) == PackageManager.PERMISSION_GRANTED }
            if (isAllPermissionsGranted) {
                permissionGranted(requestCode)
            } else {
                ActivityCompat.requestPermissions(this, permissions, requestCode)
            }
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
            permissionGranted(requestCode)
        } else {
            permissionDenied(requestCode)
        }
    }

    // 권한이 있는 경우 실행
    fun permissionGranted(requestCode: Int) {
        startProcess() // 권한이 있는 경우 구글 지도를준비하는 코드 실행
    }

    // 권한이 없는 경우 실행
    fun permissionDenied(requestCode: Int) {
        Toast.makeText(this
            , "권한 승인이 필요합니다."
            , Toast.LENGTH_LONG)
            .show()
    }

    override fun onMapReady(googleMap: GoogleMap) {
        mMap = googleMap
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
        updateLocation()
    }

    // 위치 정보를 받아오는 역할
    @SuppressLint("MissingPermission") //requestLocationUpdates는 권한 처리가 필요한데 현재 코드에서는 확인 할 수 없음. 따라서 해당 코드를 체크하지 않아도 됨.
    fun updateLocation() {
        val locationRequest = LocationRequest.create()
        locationRequest.run {
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
            interval = 1000
        }

        locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult?) {
                locationResult?.let {
                    for(location in it.locations) {
                        Log.d("Location", "${location.latitude} , ${location.longitude}")
                        setLastLocation(location)
                    }
                }
            }
        }
        fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper())
    }

    fun setLastLocation(lastLocation: Location) {
        val LATLNG = LatLng(lastLocation.latitude, lastLocation.longitude)
        val markerOptions = MarkerOptions()
            .position(LATLNG)
            .title("Here!")

        val cameraPosition = CameraPosition.Builder()
            .target(LATLNG)
            .zoom(15.0f)
            .build()
        mMap.clear()
        mMap.addMarker(markerOptions)
        mMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
    }
}

이 코드는 스틱코드를 이용하면 쉽게 불러다 쓸 수 있습니다.

https://stickode.com/detail.html?no=2205 

 

스틱코드

 

stickode.com

 

결과