1๏ธโฃ ๋ฌธ์ ์ํฉ
์๋๋ก์ด๋ ์ฑ์์ ์ฌ์ฉ์์ ํ์ฌ ์์น๋ฅผ ์ง๋์ ํ์ํ๋ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ ์ค, ๋งค๋ฒ ์์น๋ฅผ ์๋ก ๋ถ๋ฌ์ค๊ฒ ๋จ.
ํนํ, ์ฑ์ ๋ค์ ์์ํ ๋๋ง๋ค ์์น๋ฅผ ์๋ก ๊ฐ์ ธ์ค๋ ๊ณผ์ ์์ ๋๋ ์ด๊ฐ ๋ฐ์
2๏ธโฃ ๋ฌธ์ ์์ธ
์ฑ์ด ์์๋ ๋๋ง๋ค ์์น๋ฅผ ๋งค๋ฒ ์๋ก ๊ฐ์ ธ์ค๋ fusedLocationClient.getCurrentLocation() ๋ฉ์๋ ์ฌ์ฉ ํ๊ธฐ ๋๋ฌธ.
- GPS ์ ํธ๊ฐ ์ฝํ๊ฑฐ๋ ๋คํธ์ํฌ ์ํ๊ฐ ๋ถ์์ ํ ๊ฒฝ์ฐ
- ์์น ์ ๋ณด๊ฐ ์์ฃผ ๋ณ๊ฒฝ๋์ง ์๋ ์ํฉ์์๋ ๋ถํ์ํ ์์ ์๋ชจ๊ฐ ๋ฐ์
๋ฐ๋ผ์, ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์๋ ์์น ์ ๋ณด๋ฅผ ์บ์ฑํ์ฌ ์ ์ฅํ๊ณ , ํ์ํ ๋๋ง ์ ๋ฐ์ดํธํ๋ ๋ฐฉ์์ด ํ์ํฉ๋๋ค.
3๏ธโฃ ํด๊ฒฐ ๋ฐฉ๋ฒ
- SharedPreferences๋ฅผ ์ฌ์ฉํ์ฌ
- ๋ง์ง๋ง์ผ๋ก ๊ฐ์ ธ์จ ์์น๋ฅผ ์ ์ฅํ๊ณ
- ์ฑ์ด ์์๋ ๋ ์ด๋ฅผ ๋ถ๋ฌ์ ์ด๊ธฐ ์์น๋ก ์ค์
- ์บ์ฑ๋ ์์น์ ํ์ฌ ์์น๋ฅผ ๋น๊ตํ์ฌ ๋ค๋ฅธ ๊ฒฝ์ฐ์๋ง ์ง๋ ์ค์ฌ ์ ๋ฐ์ดํธ, SharedPreferences ๊ฐฑ์
์ด๋ฅผ ํตํด ์ฑ ์์ ์ ๋น ๋ฅด๊ฒ ์ง๋์ ์์น๋ฅผ ํ์ํ ์ ์์ผ๋ฉฐ, ํ์ํ ๋๋ง ์๋ก์ด ์์น ์ ๋ณด๋ฅผ ์ ๋ฐ์ดํธํ๋๋ก ํ์์ต๋๋ค.
1. SharedPreferences ์ด๊ธฐํ
val sharedPreferences = getSharedPreferences("LocationCache", MODE_PRIVATE)
- ์๋ฆฌ:
- SharedPreferences๋ ๊ฐ๋จํ ํค-๊ฐ ์์ ์ ์ฅํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
- ์ฌ๊ธฐ์๋ "LocationCache"๋ผ๋ ์ด๋ฆ์ SharedPreferences ๊ฐ์ฒด๋ฅผ ์ด๊ธฐํํ์ฌ ์์น ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ๋ถ๋ฌ์ฌ ์ ์๋๋ก ํฉ๋๋ค.
- MODE_PRIVATE๋ ์ด ๋ฐ์ดํฐ๊ฐ ์ฑ ๋ด์์๋ง ์ฌ์ฉ ๊ฐ๋ฅํ๋๋ก ์ค์ ํฉ๋๋ค.
2. ์ด์ ์ ์ ์ฅ๋ ์์น ๊ฐ์ ธ์ค๊ธฐ
val cachedLatitude = sharedPreferences.getString("latitude", null)?.toDoubleOrNull()
val cachedLongitude = sharedPreferences.getString("longitude", null)?.toDoubleOrNull()
- ์๋ฆฌ:
- ์ด์ ์ ์ ์ฅ๋ ์๋์ ๊ฒฝ๋ ๊ฐ์ SharedPreferences์์ ๋ถ๋ฌ์ต๋๋ค.
- ์ ์ฅ๋ ๊ฐ์ด ์์ ๊ฒฝ์ฐ null์ ๋ฐํํฉ๋๋ค.
- ๋ฌธ์์ด๋ก ์ ์ฅ๋ ๊ฐ์ Double๋ก ๋ณํํ์ฌ ์ฌ์ฉํ ์ ์๋๋ก ํฉ๋๋ค.
3. ์บ์ฑ๋ ์์น๋ก ์ง๋ ์ค์
if (cachedLatitude != null && cachedLongitude != null) {
val cachedPosition = LatLng.from(cachedLatitude, cachedLongitude)
kakaoMap.moveCamera(CameraUpdateFactory.newCenterPosition(cachedPosition, 16))
addMarkerToMap(cachedPosition, null)
}
- ์๋ฆฌ:
- ์ ์ฅ๋ ์์น๊ฐ ์กด์ฌํ ๊ฒฝ์ฐ,
- ์ด๋ฅผ LatLng ๊ฐ์ฒด๋ก ๋ณํํ์ฌ ์ง๋ ์ค์ฌ์ ์ค์ ํฉ๋๋ค.
- moveCamera ๋ฉ์๋๋ฅผ ํตํด ์ง๋์ ์ค์ฌ์ ์บ์ฑ๋ ์์น๋ก ์ด๋์ํค๊ณ ,
- addMarkerToMap ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ์์น์ ๋ง์ปค๋ฅผ ์ถ๊ฐํฉ๋๋ค.
- ์ ์ฅ๋ ์์น๊ฐ ์กด์ฌํ ๊ฒฝ์ฐ,
4. ์์น ๊ถํ ํ์ธ
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
// ์์น ์
๋ฐ์ดํธ ์ฃผ๊ธฐ๋ฅผ ์กฐ์ ํ์ฌ ํ์ํ ๋๋ง ์์น๋ฅผ ๊ฐ์ ธ์ด
}
- ์๋ฆฌ:
- ์ฌ์ฉ์๊ฐ ์์น ๊ถํ์ ํ์ฉํ๋์ง ํ์ธํฉ๋๋ค.
- ACCESS_FINE_LOCATION ๊ถํ์ด ํ์ฉ๋ ๊ฒฝ์ฐ์๋ง ์์น ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
- ์ด๋ ์ฌ์ฉ์ ํ๋ผ์ด๋ฒ์๋ฅผ ๋ณดํธํ๊ณ , ์ฑ์ด ๋ถํ์ํ ์ค๋ฅ๋ฅผ ๋ฐ์์ํค์ง ์๋๋ก ํฉ๋๋ค.
5-1. ํ์ฌ ์์น ์์ฒญ
fusedLocationClient.getCurrentLocation(Priority.PRIORITY_BALANCED_POWER_ACCURACY, null)
.addOnSuccessListener { location ->
// ์์น๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ๊ฐ์ ธ์์ ๋ ์คํ๋๋ ์ฝ๋
}
.addOnFailureListener {
// ์์น๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฐ ์คํจํ์ ๋ ์คํ๋๋ ์ฝ๋
}
- ์๋ฆฌ:
- fusedLocationClient.getCurrentLocation() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ํ์ฌ ์์น๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
- PRIORITY_BALANCED_POWER_ACCURACY๋ ๋ฐฐํฐ๋ฆฌ ์๋ชจ๋ฅผ ๊ณ ๋ คํ์ฌ ์ ์ ํ ์ ํ๋๋ก ์์น๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
- ์ฑ๊ณต ์: ์์น๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ๊ฐ์ ธ์ค๋ฉด addOnSuccessListener๊ฐ ์คํ๋ฉ๋๋ค
- ์คํจ ์: ์์น๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฐ ์คํจํ๋ฉด addOnFailureListener๊ฐ ์คํ๋์ด ์ฌ์ฉ์์๊ฒ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ํ์ํฉ๋๋ค.
5-2. ์์น ๊ฐ์ฒด ์ฒ๋ฆฌ
location?.let {
val currentPosition = LatLng.from(it.latitude, it.longitude)
// ์ดํ ์ฝ๋์์ currentPosition์ ์ฌ์ฉ
}
- ์๋ฆฌ:
- ์์น ๊ฐ์ฒด๊ฐ null์ด ์๋ ๊ฒฝ์ฐ์๋ง ์ฒ๋ฆฌํฉ๋๋ค.
- let ๋ธ๋ก์ ์ฌ์ฉํ์ฌ location ๊ฐ์ฒด์ ์๋์ ๊ฒฝ๋๋ฅผ LatLng ๊ฐ์ฒด๋ก ๋ณํํฉ๋๋ค.
- ์ด ๊ฐ์ฒด๋ ์ง๋ ์ค์ฌ์ ์ค์ ํ๊ฑฐ๋ ๋ง์ปค๋ฅผ ์ถ๊ฐํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
5.3 ์์น ๋ณ๊ฒฝ ์ฌ๋ถ ํ๋จ
val isLocationChanged = cachedPosition?.let { cachedPos ->
val cachedLocation = Location("").apply {
latitude = cachedPos.latitude
longitude = cachedPos.longitude
}
val currentLocation = Location("").apply {
latitude = it.latitude
longitude = it.longitude
}
cachedLocation.distanceTo(currentLocation) > 10 // 10 meters threshold
} ?: true
- ์๋ฆฌ:
- ์บ์ฑ๋ ์์น์ ํ์ฌ ์์น ๋น๊ต: Location ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ๋ ์์น ๊ฐ์ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ์ฐํฉ๋๋ค.
- ๊ฑฐ๋ฆฌ ๊ธฐ์ค: distanceTo() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๋ ์์น ๊ฐ์ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ์ฐํ๊ณ , ์ด ๊ฐ์ด 10๋ฏธํฐ๋ณด๋ค ํฐ ๊ฒฝ์ฐ์๋ง ์์น๊ฐ ๋ณ๊ฒฝ๋ ๊ฒ์ผ๋ก ํ๋จํฉ๋๋ค.
- ๊ธฐ๋ณธ๊ฐ: ์บ์ฑ๋ ์์น๊ฐ ์์ ๊ฒฝ์ฐ(cachedPosition์ด null์ธ ๊ฒฝ์ฐ), ์์น๊ฐ ๋ณ๊ฒฝ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผํ์ฌ true๋ฅผ ๋ฐํํฉ๋๋ค.
5.4 ์์น ๋ณ๊ฒฝ ์ ์ง๋ ์ ๋ฐ์ดํธ
if (isLocationChanged) {
// ํ์ฌ ์์น๋ก ์ง๋ ์ค์ฌ ์ค์
kakaoMap.moveCamera(CameraUpdateFactory.newCenterPosition(currentPosition, 16))
addMarkerToMap(currentPosition, null)
// ์๋ก์ด ์์น๋ฅผ ์บ์ฑ
val editor = sharedPreferences.edit()
editor.putString("latitude", it.latitude.toString())
editor.putString("longitude", it.longitude.toString())
editor.apply()
}
- ์๋ฆฌ:
- ์ง๋ ์
๋ฐ์ดํธ:
- ์์น๊ฐ ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ์๋ง moveCamera ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ง๋์ ์ค์ฌ์ ์๋ก์ด ์์น๋ก ์ค์ ํ๊ณ ,
- addMarkerToMap ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ์์น์ ๋ง์ปค๋ฅผ ์ถ๊ฐํฉ๋๋ค.
- ์์น ์ ์ฅ:
- SharedPreferences์ ์๋ก์ด ์์น๋ฅผ ์ ์ฅํ์ฌ ์ดํ์ ์ฑ์ด ๋ค์ ์์๋ ๋ ์ด ์์น๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ํฉ๋๋ค.
- apply() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๋น๋๊ธฐ์ ์ผ๋ก ์ ์ฅํฉ๋๋ค.
- ์ง๋ ์
๋ฐ์ดํธ:
4๏ธโฃ ๊ทธ ๊ณผ์ ์์ ๋ฐ์ํ ๋ฌธ์
- LatLng ๊ฐ์ฒด ์์ฑ ์๋ฌ:
cannot access "init" it is private in LatLng
kakao.vector~ importํด๋ ๊ณ์ ์๋ฌ ๋จ.
LatLng(a,b) -> LatLng.from(a,b) ๋ก ํด๊ฒฐ
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด from() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ฒด๋ฅผ ์์ฑํ์ต๋๋ค.
- ์์น ๊ถํ ๋ฌธ์ : ์์น ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด์๋ ๋ฐ๋์ ์์น ๊ถํ์ด ํ์ํฉ๋๋ค. ๊ถํ์ด ์๋ ๊ฒฝ์ฐ ์ฑ์ด ์ ์์ ์ผ๋ก ์๋ํ์ง ์์ ์ ์์ต๋๋ค.
- ์์น ์ ๋ฐ์ดํธ ์ฃผ๊ธฐ ์ค์ : ๋๋ฌด ์์ฃผ ์์น๋ฅผ ์ ๋ฐ์ดํธํ๋ฉด ๋ฐฐํฐ๋ฆฌ ์๋ชจ๊ฐ ์ฆ๊ฐํ ์ ์์ต๋๋ค. ์ ์ ํ ์ฃผ๊ธฐ๋ก ์์น๋ฅผ ์ ๋ฐ์ดํธํ๋๋ก ์ค์ ํด์ผ ํฉ๋๋ค.
5๏ธโฃ ์ ์ฒด์ฝ๋
๊ฐ์ ์ฝ๋ ์
private fun setMapToCurrentLocation() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
fusedLocationClient.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, null)
.addOnSuccessListener { location ->
location?.let {
val currentPosition = LatLng(it.latitude, it.longitude)
kakaoMap.moveCamera(CameraUpdateFactory.newCenterPosition(currentPosition, 16))
addMarkerToMap(currentPosition, null)
}
}
}
}
import android.location.Location
private fun setMapToCurrentLocation() {
val sharedPreferences = getSharedPreferences("LocationCache", MODE_PRIVATE)
// ์ด์ ์ ์ ์ฅ๋ ์์น ๊ฐ์ ธ์ค๊ธฐ (์๋, ๊ฒฝ๋)
val cachedLatitude = sharedPreferences.getString("latitude", null)?.toDoubleOrNull()
val cachedLongitude = sharedPreferences.getString("longitude", null)?.toDoubleOrNull()
var cachedPosition: LatLng? = null
if (cachedLatitude != null && cachedLongitude != null) {
cachedPosition = LatLng.from(cachedLatitude, cachedLongitude)
kakaoMap.moveCamera(CameraUpdateFactory.newCenterPosition(cachedPosition, 16))
addMarkerToMap(cachedPosition, null)
}
// ์์น ๊ถํ์ด ํ์ฉ๋์๋์ง ํ์ธ
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
fusedLocationClient.getCurrentLocation(Priority.PRIORITY_BALANCED_POWER_ACCURACY, null)
.addOnSuccessListener { location ->
location?.let {
val currentPosition = LatLng.from(it.latitude, it.longitude)
// ์ด์ ์์น์ ํ์ฌ ์์น ๋น๊ต, ๊ฑฐ๋ฆฌ ์ฐจ๊ฐ 10๋ฏธํฐ ์ด์์ธ ๊ฒฝ์ฐ๋ง
//์ด๊ธฐ๊ฐ์ด null์ธ ๊ฒฝ์ฐ๋ ๋ฐ๋ก true
val isLocationChanged = cachedPosition?.let { cachedPos ->
val cachedLocation = Location("").apply {
latitude = cachedPos.latitude
longitude = cachedPos.longitude
}
val currentLocation = Location("").apply {
latitude = it.latitude
longitude = it.longitude
}
cachedLocation.distanceTo(currentLocation) > 10 // 10 meters threshold
} ?: true
if (isLocationChanged) {
// ํ์ฌ ์์น๋ก ์ง๋ ์ค์ฌ ์ค์
kakaoMap.moveCamera(CameraUpdateFactory.newCenterPosition(currentPosition, 16))
addMarkerToMap(currentPosition, null)
// ์๋ก์ด ์์น๋ฅผ ์บ์ฑ
val editor = sharedPreferences.edit()
editor.putString("latitude", it.latitude.toString())
editor.putString("longitude", it.longitude.toString())
editor.apply()
}
}
}
.addOnFailureListener {
Toast.makeText(this, "ํ์ฌ ์์น๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.", Toast.LENGTH_SHORT).show()
}
}
}
์ฌ๊ธฐ์ ์ด addMarkToMap์ด 2๋ฒ ํธ์ถ๋๋๋ฐ,
1๋ฒ addMarkToMap์์ ์บ์ฑ๋ ์์น๋ก ๋ง์ปค ์ถ๊ฐ,
2๋ฒ addMarkToMap์ผ๋ก ์ฐ์น ์ฐจ์ด๊ฐ 10์ด์์ธ ๊ฒฝ์ฐ ์ง๋์ ๋ค์ ๋งํฌ๋ฅผ ์ถ๊ฐ(์ ๋ฐ์ดํธ), SharedPreferences ๊ฐ ๊ฐฑ์
