[Android ๊ธฐ์ดˆ] 18. ๊ตฌ๊ธ€ ์ง€๋„์•ฑ ์ œ์ž‘
๋ฐ˜์‘ํ˜•

 

 

 

 

 

 

 

1. ์ง€๋„ ์‚ฌ์šฉ ์„ค์ •ํ•˜๊ธฐ

implementation 'com.google.android.gms:play-services-maps:18.1.0'
implementation 'com.google.android.gms:play-services-location:21.0.1'
  • Gradle์˜ dependencies ํ•ญ๋ชฉ ์„ค์ •

 

<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"/>
  • Manifest ํŒŒ์ผ์— permission ์„ค์ •

 

<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๋ฅผ ์ด์šฉํ•˜๋Š” ํ‚ค๋ฅผ ๋“ฑ๋ก

 

 

 

 

2. ๊ตฌ๊ธ€ ๊ฐœ๋ฐœ์ž ์ฝ˜์†”์—์„œ ์ง€๋„ API ํ‚ค ์–ป๊ธฐ

<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"/>
  • ๋ ˆ์ด์•„์›ƒ XML ์œ„์™€ ๊ฐ™์ด ์„ค์ •
  • ๊ตฌ๊ธ€ ๊ฐœ๋ฐœ์ž ์ฝ˜์†”([console.cloud.google.com](http://console.cloud.google.com/) ์ ‘์† ํ›„
    ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด๋ฅผ ๋งŒ๋“ค๋ฉด ์ง€๋„ API ํ‚ค ๋ฅผ ๋ฐœ๊ธ‰
  • ๊ตฌ๊ธ€ ๊ฐœ๋ฐœ์ž ์ฝ˜์†”์—์„œ ์–ป์€ ์ง€๋„ APIํ‚ค๋ฅผ Manifest ํŒŒ์ผ์— ๋“ฑ๋ก

 

 

 

 

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?.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 ->
        }
  • ์ง€๋„์—์„œ ์‚ฌ์šฉ์ž ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ
        - GoogleMap.OnMapClickListener: ์ง€๋„ ํด๋ฆญ ์ด๋ฒคํŠธ
        - GoogleMap.OnMapLongClickListener: ์ง€๋„ ๋กฑ ํด๋ฆญ ์ด๋ฒคํŠธ
        - GoogleMap.OnMarkerClickListener: ๋งˆ์ปค ํด๋ฆญ ์ด๋ฒคํŠธ
        - GoogleMap.OnMarkerDragListener: ๋งˆ์ปค ๋“œ๋ž˜๊ทธ ์ด๋ฒคํŠธ
        - GoogleMap.OnInfoWindowClickListener: ์ •๋ณด ์ฐฝ ํด๋ฆญ ์ด๋ฒคํŠธ
        - GoogleMap.OnCameraIdleListener: ์ง€๋„ ํ™”๋ฉด ๋ณ€๊ฒฝ ์ด๋ฒคํŠธ



 

4. GoogleCloud์—์„œ ์‚ฌ์šฉ์ž ์ธ์ฆํ‚ค ๋งŒ๋“ค๊ธฐ

 

(1) GoogleCloud์— ์ ‘์†ํ•˜์—ฌ ๊ตฌ๊ธ€ ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธ

 

Google ํด๋ผ์šฐ๋“œ ํ”Œ๋žซํผ

๋กœ๊ทธ์ธ Google ํด๋ผ์šฐ๋“œ ํ”Œ๋žซํผ์œผ๋กœ ์ด๋™

accounts.google.com

 

(2) ์ƒˆ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

(3) API ๋ฐ ์„œ๋น„์Šค -> ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด

(4) ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด ๋งŒ๋“ค๊ธฐ + ๋ฒ„ํŠผ ํด๋ฆญ > API ํ‚ค ํด๋ฆญ

(5) API ํ‚ค ์ƒ์„ฑ

 

 

 

5. Android APP ๋งŒ๋“ค๊ธฐ (์˜ˆ์‹œ)

๋”๋ณด๊ธฐ

 

1. ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

2. build.gradle ํŒŒ์ผ์— Googlemap ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€

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'
}

 

3. AndriodManifest.xml ์— permission๊ณผ google-map api ์ถ”๊ฐ€
googlecloud์—์„œ ์ƒ์„ฑํ•œ 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">

    <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">
        
        <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>

 

activity_main.xml

<?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"/>

 

MainActivity.kt

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


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

        //๊ถŒํ•œ ์š”์ฒญ
        locationPermission.launch(
            arrayOf(
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_FINE_LOCATION
            )
        )
    }


    // ์ง€๋„ ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์ด ๋  ๋•Œ
    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 ์œผ๋กœ ์ „๋‹ฌ๋œ๋‹ค.
            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))
    }
}

 

 

 

 

 

 

๋ฐ˜์‘ํ˜•
 ๐Ÿ’ฌ C O M M E N T