Android

Android Studio_ 10. 구글 지도앱 만들기

아이른 2024. 5. 30. 23:41

1. GoogleCloud에서 사용자 인증키 만들기

환영합니다 – donlate – Google Cloud Console

 

Google 클라우드 플랫폼

로그인 Google 클라우드 플랫폼으로 이동

accounts.google.com

  • GoogleCloud 접속

  • 새 프로젝트 생성

 

  • 사용자 인증정보 만들기 > API키 > API 키 생성

 

2. 지도 사용 설정하기

  • build.gradle
implementation ("com.google.android.gms:play-services-maps:18.1.0")
implementation ("com.google.android.gms:play-services-location:21.0.1")
  • manifest > 권한 및 구글 지도 API를 이용하는 키를 등록
...
<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"/>


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

 

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: 지도 화면 변경 이벤트
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 ->
        }

 

4. 구현

  • 예제에선 Activity에서 구현하였지만 Fragment에서 구현해보기로 함
더보기
  • fragment_search_place.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".searchPlace.SearchPlaceFragment">

    <fragment
        android:id="@+id/mv_searchResultMapView"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="350dp"
        android:layout_height="550dp"
        android:layout_marginTop="50dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/et_placeSeachBox" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

  • SearchPlaceFragment.kt
package com.example.donotlate.searchPlace

import android.Manifest
import android.content.pm.PackageManager
import android.location.Location
import android.os.Bundle
import android.os.Looper
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import com.example.donotlate.R
import com.example.donotlate.databinding.FragmentSearchPlaceBinding
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
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.MapView
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

class SearchPlaceFragment : Fragment(), OnMapReadyCallback {

    private lateinit var mGoogleMap: GoogleMap

    //위치 서비스가 gps를 사용해서 위치를 확인
    lateinit var fusedLocationClient: FusedLocationProviderClient

    //위치 값 요청에 대한 갱신 정보를 받는 변수
    lateinit var locationCallback: LocationCallback

    lateinit var locationPermission: ActivityResultLauncher<Array<String>>


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_search_place, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        locationPermission = registerForActivityResult(
            ActivityResultContracts.RequestMultiplePermissions()
        ) { results ->
            if (results.all { it.value }) {
                (childFragmentManager.findFragmentById(R.id.mv_searchResultMapView) as SupportMapFragment)!!.getMapAsync(
                    this
                )//fragment안의 fragment를 찾기 때문에 childFragmentManager

//                (parentFragmentManager.findFragmentById(R.id.mv_searchResultMapView) as SupportMapFragment)!!.getMapAsync(
//                    this
//                )//name="com.google.android.gms.maps.SupportMapFragment" 지정했기 때문에 .SupportMapFragment로 타입캐스팅
            } else { //문제가 발생했을 때
            }
        }
        //권한 요청
        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(requireActivity())
        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(
                requireContext(),
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
                requireContext(),
                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))
    }


}