Google Map Api 사용 방법은 많은 블로그에 나와 있어서 생략 !
Google Map Api Key 발급 & 적용
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
...
// 대략적인 위치 정보 권한 (3km 이내)
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
// 정확한 위치 정보 권한 (50m 이내)
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
...
</manifest>
manifest 에서 위 2개의 코드를 추가해 준다.
dependencies {
...
implementation 'com.google.android.gms:play-services-location:21.0.1'
...
}
private val permissionRequest = 99
private var permissions = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
override fun onCreate(savedInstanceState: Bundle?) {
// 권한이 없다면 권한 요청을 한다.
if (!isPermitted()) {
ActivityCompat.requestPermissions(this, permissions, permissionRequest)
}
}
private fun isPermitted(): Boolean {
for (perm in permissions) {
if (ContextCompat.checkSelfPermission(this, perm)
!= PackageManager.PERMISSION_GRANTED
) {
return false
}
}
return true
}
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
mMap.setMinZoomPreference(5.0F) // 지도의 초대 크기
mMap.setMaxZoomPreference(20.0F) // 지도의 최소 크기
fusedLocationProviderClient =
LocationServices.getFusedLocationProviderClient(this) //gps 자동으로 받아오기
setUpdateLocationListener() // 현제 위치 업데이트
}
@SuppressLint("MissingPermission")
fun setUpdateLocationListener() {
val locationRequest =
LocationRequest.Builder(
PRIORITY_HIGH_ACCURACY, //높은 정확도
1000 //1초에 한번씩 GPS 요청
).build()
//location 요청 함수 호출 (locationRequest, locationCallback)
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
locationResult.locations.withIndex().forEach {
setLastLocation(it.value)
}
}
}
//location 요청 함수 호출 (locationRequest, locationCallback)
fusedLocationProviderClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.myLooper()
)
}
fun setLastLocation(location: Location) {
val myLocation = LatLng(location.latitude, location.longitude)
val markerOptions =
MarkerOptions()
.position(myLocation)
.title("현재 위치")
mMap.addMarker(markerOptions)
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(myLocation, 17.0F)) // 현재 위치, 지도 크기
}
val markerOptions =
MarkerOptions()
.position(myLocation)
.title("현재 위치")
.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_baseline_location_on_24))
단, Vector 이미지를 사용할 경우 아래위 같은 Exception을 뱉는다.
com.google.maps.api.android.lib6.common.apiexception.b: Failed to decode image. The provided image must be a Bitmap.
아래와 같은 코드로 Vector Image를 BitmapDescriptor 로 변환해서 사용
// 벡터 이미지 변환
private fun bitmapDescriptorFromVector(context: Context, vectorResId: Int): BitmapDescriptor? {
return ContextCompat.getDrawable(context, vectorResId)?.run {
setBounds(0, 0, intrinsicWidth, intrinsicHeight)
val bitmap =
Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888)
draw(Canvas(bitmap))
BitmapDescriptorFactory.fromBitmap(bitmap)
}
}
val markerOptions =
MarkerOptions()
.position(myLocation)
.title("현재 위치")
.icon(bitmapDescriptorFromVector(this, R.drawable.ic_baseline_location_on_24))
private var markerState: Marker? = null
private var baseMarker: BitmapDescriptor? = null
private var selectMarker: BitmapDescriptor? = null
override fun onCreate(savedInstanceState: Bundle?) {
...
baseMarker = bitmapDescriptorFromVector(this, R.drawable.ic_baseline_location_on_24)
selectMarker = bitmapDescriptorFromVector(this, R.drawable.ic_baseline_location_click)
...
setMarkerClickListener()
}
private fun setMarkerClickListener() {
mMap.setOnMarkerClickListener { marker ->
if (markerState != null && markerState != marker) {
clearMarkerClick(checkNotNull(markerState))
marker.setIcon(selectMarker)
markerState = marker
} else {
marker.setIcon(selectMarker)
markerState = marker
}
// false : 마커 클릭 이벤트의 기본 동작 수행 (클릭시 카메라 이동, title 띄우기 등)
false
}
}
private fun clearMarkerClick(marker: Marker) {
marker.setIcon(baseMarker)
}
private fun setMapClickListener() {
mMap.setOnMapClickListener {
if (markerState != null) {
markerState?.setIcon(baseMarker)
markerState = null
}
}
}
package boostcamp.search.googlemapexample
import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Canvas
import android.location.Location
import android.os.Bundle
import android.os.Looper
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import boostcamp.search.googlemapexample.databinding.ActivityMapsBinding
import com.google.android.gms.location.*
import com.google.android.gms.location.Priority.PRIORITY_HIGH_ACCURACY
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.*
class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
private val permissionRequest = 99
private lateinit var mMap: GoogleMap
private lateinit var binding: ActivityMapsBinding
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
lateinit var locationCallback: LocationCallback
private var permissions = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
private var markerState: Marker? = null
private var baseMarker: BitmapDescriptor? = null
private var selectMarker: BitmapDescriptor? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMapsBinding.inflate(layoutInflater)
setContentView(binding.root)
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
if (!isPermitted()) {
ActivityCompat.requestPermissions(this, permissions, permissionRequest)
}
baseMarker = bitmapDescriptorFromVector(this, R.drawable.ic_baseline_location_on_24)
selectMarker = bitmapDescriptorFromVector(this, R.drawable.ic_baseline_location_click)
}
private fun isPermitted(): Boolean {
for (perm in permissions) {
if (ContextCompat.checkSelfPermission(this, perm)
!= PackageManager.PERMISSION_GRANTED
) {
return false
}
}
return true
}
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
mMap.setMinZoomPreference(5.0F)
mMap.setMaxZoomPreference(20.0F)
fusedLocationProviderClient =
LocationServices.getFusedLocationProviderClient(this) //gps 자동으로 받아오기
setUpdateLocationListener()
setMarkerClickListener()
setMapClickListener()
}
@SuppressLint("MissingPermission")
fun setUpdateLocationListener() {
val locationRequest =
LocationRequest.Builder(PRIORITY_HIGH_ACCURACY, 50000).build()
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
locationResult.locations.withIndex().forEach {
setLastLocation(it.value)
}
}
}
//location 요청 함수 호출 (locationRequest, locationCallback)
fusedLocationProviderClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.myLooper()
)
}
fun setLastLocation(location: Location) {
val myLocation = LatLng(location.latitude, location.longitude)
val myLocation2 = LatLng(location.latitude + 0.001, location.longitude + 0.001)
val markerOptions =
MarkerOptions()
.position(myLocation)
.title("현재 위치")
.icon(baseMarker)
val markerOptions2 =
MarkerOptions()
.position(myLocation2)
.title("현재 위치")
.icon(baseMarker)
mMap.addMarker(markerOptions)
mMap.addMarker(markerOptions2)
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(myLocation, 17.0F))
}
private fun setMarkerClickListener() {
mMap.setOnMarkerClickListener { marker ->
if (markerState != null && markerState != marker) {
clearMarkerClick(checkNotNull(markerState))
marker.setIcon(selectMarker)
markerState = marker
} else {
marker.setIcon(selectMarker)
markerState = marker
}
// 마커 클릭 이벤트의 기본 동작 수행 (클릭시 카메라 이동, title 띄우기 등)
false
}
}
private fun setMapClickListener() {
mMap.setOnMapClickListener {
if (markerState != null) {
markerState?.setIcon(baseMarker)
markerState = null
}
}
}
private fun clearMarkerClick(marker: Marker) {
marker.setIcon(baseMarker)
}
// 벡터 이미지 변환
private fun bitmapDescriptorFromVector(context: Context, vectorResId: Int): BitmapDescriptor? {
return ContextCompat.getDrawable(context, vectorResId)?.run {
setBounds(0, 0, intrinsicWidth, intrinsicHeight)
val bitmap =
Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888)
draw(Canvas(bitmap))
BitmapDescriptorFactory.fromBitmap(bitmap)
}
}
}