Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

부산대 Android_이지은 4주차 1단계 #60

Open
wants to merge 21 commits into
base: jieunyume
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a749e88
docs: README.md 작성
JieunYume Jul 16, 2024
80c47d6
refactor: SavedLocationHolder의 뷰 변수를 by lazy로 변경
JieunYume Jul 17, 2024
3abbc9b
feat: 저장된 검색어의 제목(title)을 전달하는 콜백 함수 구현
JieunYume Jul 17, 2024
b0527b5
feat: itemView에 콜백 함수 적용
JieunYume Jul 17, 2024
adca161
feat: MainActivity에서 콜백 함수 구현해서 LocationViewModel의 searchLocationsFro…
JieunYume Jul 17, 2024
6d7449b
refactor: handleNoResultMessage() 함수를 만들어서 중복 코드 제거
JieunYume Jul 17, 2024
e533b25
feat: searchEditText의 text를 저장된 검색어의 제목(title)로 설정
JieunYume Jul 17, 2024
2eb7b23
feat: searchEditText의 커서 위치 변경
JieunYume Jul 17, 2024
0b3db81
refactor: 콜백 함수 이름을 네이밍 컨벤션에 맞게 변경
JieunYume Jul 17, 2024
193fa3c
refactor: LocationLocalDataSource에서 중복 코드를 함수로 분리
JieunYume Jul 17, 2024
681b945
feat: 키워드로 장소 검색하는 api에서 x, y 값 받아오기
JieunYume Jul 17, 2024
88faf63
feat: 검색 결과 목록 중 하나의 항목을 선택하면 MapActivity로 전환되게 구현
JieunYume Jul 17, 2024
66c2ece
feat: mapView의 좌표를 검색 결과 목록 중 선택된 항목의 좌표로 설정
JieunYume Jul 17, 2024
4f44f65
feat: 검색 결과 목록 중 선택된 항목의 좌표에 KakaoMap label을 추가
JieunYume Jul 18, 2024
97a4556
feat: SharedPreferences에 마지막 좌표 저장 및 불러오기 기능 구현
JieunYume Jul 18, 2024
e1786ed
feat: onMapError() 호출 시 에러 화면 노출
JieunYume Jul 18, 2024
c3d8a4a
feat: MapActivity에서 BottomSheet를 사용하여 위치 정보 노출
JieunYume Jul 18, 2024
038d548
refactor: 뷰 변수들을 by lazy로 정의하도록 변경
JieunYume Jul 18, 2024
116e601
refactor: onMapError()에서 에러 메세지를 띄울 때 스레드를 runOnUiThread로 변경
JieunYume Jul 18, 2024
8b2cd64
refactor: 저장된 데이터가 없을 때 null 처리로 BottomSheet가 뜨지 않게 설정
JieunYume Jul 18, 2024
b99401c
refactor: onMapReady에서 함수 분리
JieunYume Jul 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
# android-map-location
# android-map-location
## step1 기능 목록
1. 저장된 검색어 목록에 기능 추가하기
- 저장된 검색어 중 하나를 선택하면 해당 검색어의 검색 결과 목록이 표시된다.
2. 검색 결과 목록에 기능 추가하기
- 검색 결과 목록 중 하나의 항목을 선택하면 해당 항목의 위치를 지도에 표시한다.
3. 마지막 위치 저장하기
- 앱 종료 시 마지막 위치를 SharedPreference 저장하여 다시 앱 실행 시 해당 위치로 포커스 한다.
4. 에러 처리하기
- 카카오지도 onMapError() 호출 시 에러 화면을 보여준다. "지도 인증을 실패했습니다. 다시 시도해주세요. '에러이름(에러코드): 에러메세지'
6 changes: 4 additions & 2 deletions app/src/main/java/campus/tech/kakao/map/model/Location.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package campus.tech.kakao.map.model
data class Location(
val title: String,
val address: String,
val category: String
val category: String,
val longitude: String,
val latitude: String
Comment on lines +7 to +8

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lat lng 는 double 이어도 되지 않을까요!?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코드에서 위치 정보를 미리 변환하지 않고 실제로 사용할 때마다 변환하려고 했습니다! 근데 생각해보니까 그러면 여러 번 변환하는 경우가 생길 수 있으니까 더 비효율적일 수 있겠네요! 감사합니다~!

){
companion object {
fun LocationDto.toLocation(): Location {
return Location(title, address, category)
return Location(title, address, category, x, y)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,9 @@ data class LocationDto(
@SerializedName("category_group_name")
val category: String,
@SerializedName("address_name")
val address: String
val address: String,
@SerializedName("x")
val x: String,
@SerializedName("y")
val y: String
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package campus.tech.kakao.map.model.datasource

import android.content.ContentValues
import android.database.Cursor
import android.util.Log
import campus.tech.kakao.map.model.Contract.LocationEntry
import campus.tech.kakao.map.model.Contract.SavedLocationEntry
Expand Down Expand Up @@ -46,16 +47,21 @@ class LocationLocalDataSource(private val dbHelper : LocationDbHelper) {
val results = mutableListOf<Location>()
with(cursor) {
while (moveToNext()) {
val title = getString(getColumnIndexOrThrow(LocationEntry.COLUMN_NAME_TITLE))
val address = getString(getColumnIndexOrThrow(LocationEntry.COLUMN_NAME_ADDRESS))
val category = getString(getColumnIndexOrThrow(LocationEntry.COLUMN_NAME_CATEGORY))
results.add(Location(title, address, category))
val location = getLocation()
results.add(location)
}
}
cursor.close()
return results
}

private fun Cursor.getLocation(): Location {
val title = getString(getColumnIndexOrThrow(LocationEntry.COLUMN_NAME_TITLE))
val address = getString(getColumnIndexOrThrow(LocationEntry.COLUMN_NAME_ADDRESS))
val category = getString(getColumnIndexOrThrow(LocationEntry.COLUMN_NAME_CATEGORY))
return Location(title, address, category, "", "")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

latitude longitude 는 없어도 될까요?!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2주차 과제에서 쓰인 코드를 그대로 뒀었는데, 삭제하도록 하겠습니다!

}

fun addSavedLocation(title: String): Long {
val db = dbHelper.writableDatabase
val values = ContentValues().apply {
Expand Down Expand Up @@ -129,10 +135,8 @@ class LocationLocalDataSource(private val dbHelper : LocationDbHelper) {
val results = mutableListOf<Location>()
with(cursor) {
while (moveToNext()) {
val title = getString(getColumnIndexOrThrow(LocationEntry.COLUMN_NAME_TITLE))
val address = getString(getColumnIndexOrThrow(LocationEntry.COLUMN_NAME_ADDRESS))
val category = getString(getColumnIndexOrThrow(LocationEntry.COLUMN_NAME_CATEGORY))
results.add(Location(title, address, category))
val location = getLocation()
results.add(location)
}
}
cursor.close()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package campus.tech.kakao.map.view.map

data class Coordinates(val title: String, val longitude: Double, val latitude: Double, val address: String)
153 changes: 141 additions & 12 deletions app/src/main/java/campus/tech/kakao/map/view/map/MapActivity.kt
Original file line number Diff line number Diff line change
@@ -1,35 +1,61 @@
package campus.tech.kakao.map.view.map

import android.app.Activity
import android.content.Intent
import android.content.SharedPreferences
import android.graphics.Color
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import campus.tech.kakao.map.BuildConfig
import campus.tech.kakao.map.R
import campus.tech.kakao.map.view.search.MainActivity
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.kakao.vectormap.KakaoMap
import com.kakao.vectormap.KakaoMapReadyCallback
import com.kakao.vectormap.KakaoMapSdk
import com.kakao.vectormap.LatLng
import com.kakao.vectormap.MapLifeCycleCallback
import com.kakao.vectormap.MapView
import com.kakao.vectormap.label.LabelOptions
import com.kakao.vectormap.label.LabelStyle
import com.kakao.vectormap.label.LabelStyles


class MapActivity : AppCompatActivity() {
private lateinit var searchEditText: EditText
private lateinit var mapView: MapView
private val searchEditText by lazy { findViewById<EditText>(R.id.SearchEditTextInMap) }
private val mapView by lazy { findViewById<MapView>(R.id.map_view) }
private val bottomSheetLayout by lazy { findViewById<ConstraintLayout>(R.id.bottom_sheet_layout) }
private val bottom_sheet_title by lazy { findViewById<TextView>(R.id.bottom_sheet_title) }
private val bottom_sheet_address by lazy { findViewById<TextView>(R.id.bottom_sheet_address) }
private val errorMessageTextView by lazy { findViewById<TextView>(R.id.errorMessageTextView) }
private val bottomSheetBehavior: BottomSheetBehavior<ConstraintLayout> by lazy { BottomSheetBehavior.from(bottomSheetLayout) }

companion object{
private val DEFAULT_LONGITUDE = 127.115587
private val DEFAULT_LATITUDE = 37.406960
}

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

initViews()
setupEditText()
setupMapView()
}

private fun initViews() {
searchEditText = findViewById(R.id.SearchEditTextInMap)
mapView = findViewById(R.id.map_view)
override fun onResume() {
super.onResume()
mapView.resume() // MapView 의 resume 호출
}

override fun onPause() {
super.onPause()
mapView.pause() // MapView 의 pause 호출
}

private fun setupEditText() {
Expand All @@ -43,19 +69,122 @@ class MapActivity : AppCompatActivity() {
KakaoMapSdk.init(this, BuildConfig.KAKAO_API_KEY);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저번에 코멘트를 드리지 못한 부분이긴한데, sdk 의 initialize 는 activity 에서 하기보다 Application 에서 처리하기를 권장하고 있습니다! (kakao map 도 마찬가지네요!)

mapView.start(object : MapLifeCycleCallback() {
override fun onMapDestroy() {
// 지도 API 가 정상적으로 종료될 때 호출됨
Log.d("jieun", "onMapDestroy")
}

override fun onMapError(error: Exception) {
// 인증 실패 및 지도 사용 중 에러가 발생할 때 호출됨
override fun onMapError(error: Exception) { // 인증 실패 및 지도 사용 중 에러가 발생할 때 호출됨
Log.d("jieun", "onMapError" + error)
showErrorMessage(error)
}
}, object : KakaoMapReadyCallback() {
override fun onMapReady(kakaoMap: KakaoMap) {
// 인증 후 API 가 정상적으로 실행될 때 호출됨
Log.d("jieun", "onMapReady")
val coordinates = getCoordinates()
override fun onMapReady(kakaoMap: KakaoMap) { // 인증 후 API 가 정상적으로 실행될 때 호출됨
Log.d("jieun", "onMapReady coordinates: " + coordinates.toString())
if (coordinates != null) {
showLabel(coordinates, kakaoMap)
showBottomSheet(coordinates)
setSharedData("pref", coordinates)
// Log.d("jieun", "onMapReady setSharedData: " + getSharedData("pref"))
} else{
hideBottomSheet()
}
}

override fun getPosition(): LatLng {
// Log.d("jieun", "getPosition coordinates: " + coordinates.toString())
if (coordinates != null) {
return LatLng.from(coordinates.latitude, coordinates.longitude)
} else{
return LatLng.from(DEFAULT_LATITUDE, DEFAULT_LONGITUDE)
}

}

})
}

private fun showErrorMessage(error: Exception) {
runOnUiThread {
setContentView(R.layout.error_map)
errorMessageTextView.text = "지도 인증을 실패했습니다.\n다시 시도해주세요.\n\n" + error.message
}
}

private fun showLabel(
coordinates: Coordinates,
kakaoMap: KakaoMap
) {
val labelStyles: LabelStyles = LabelStyles.from(
LabelStyle.from(R.drawable.location_red_icon_resized).setZoomLevel(8),
LabelStyle.from(R.drawable.location_red_icon_resized)
.setTextStyles(32, Color.BLACK, 1, Color.GRAY).setZoomLevel(15)
)
val position = LatLng.from(coordinates.latitude, coordinates.longitude)
kakaoMap.labelManager?.getLayer()?.addLabel(
LabelOptions.from(position)
.setStyles(labelStyles)
.setTexts(coordinates.title)
)
}

private fun hideBottomSheet() {
runOnUiThread { bottomSheetLayout.visibility = View.GONE }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onMapReady 가 혹시 ui thread 가 아닌 다른 스레드에서 처리하고 있나요?

runOnUiThread 는 꼭 필요한 경우에만 처리해주시는 것이 좋습니다!

}

private fun showBottomSheet(coordinates: Coordinates) {
runOnUiThread {
bottomSheetLayout.visibility = View.VISIBLE
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
bottom_sheet_title.text = coordinates.title
bottom_sheet_address.text = coordinates.address
}
}

private fun getCoordinates(): Coordinates? {
var coordinates = getCoordinatesByIntent()
if(coordinates == null) {
coordinates = getCoordinatedBySharedPreference("pref")
}
return coordinates

}

private fun getCoordinatesByIntent(): Coordinates? {
if (intent.hasExtra("title") && intent.hasExtra("longitude")
&& intent.hasExtra("latitude") && intent.hasExtra("address")) {
val title = intent.getStringExtra("title").toString()
val longitudeString = intent.getStringExtra("longitude")
val latitudeString = intent.getStringExtra("latitude")
val address = intent.getStringExtra("address").toString()
if (longitudeString != null && latitudeString != null) {
val longitude = longitudeString.toDouble()
val latitude = latitudeString.toDouble()
return Coordinates(title, longitude, latitude, address)
} else return null
} else return null
}

fun setSharedData(name: String, coordinates: Coordinates?) {
if (coordinates != null) {
var pref: SharedPreferences = getSharedPreferences(name, Activity.MODE_PRIVATE)
var editor: SharedPreferences.Editor = pref.edit()
editor.putString("longitude", coordinates.longitude.toString())
editor.putString("latitude", coordinates.latitude.toString())
editor.putString("title", coordinates.title.toString())
editor.putString("address", coordinates.address.toString())
editor.apply()
Comment on lines +169 to +175

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 정보를 가지고 오는 것은 data layer 에서 로직이 있는 것이 좋습니다. Repository 로 로직을 옮기고, ViewModel 을 통해서 데이터를 가져오는 것을 추천드립니다!

}
}

fun getCoordinatedBySharedPreference(name: String): Coordinates? {
var pref: SharedPreferences = getSharedPreferences(name, Activity.MODE_PRIVATE)
if(pref.getString("title", "") == ""){
return null
}
val title = pref.getString("title", "").toString()
val longitude = pref.getString("longitude", "").toString().toDouble()
val latitude = pref.getString("latitude", "").toString().toDouble()
val address = pref.getString("address", "").toString()
return Coordinates(title, longitude, latitude, address)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ class LocationAdapter(

init {
itemView.setOnClickListener {
itemSelectedListener.addSavedLocation(getItem(bindingAdapterPosition).title)
val location = getItem(bindingAdapterPosition)
itemSelectedListener.onLocationViewClicked(location.title, location.longitude, location.latitude, location.address)
}
}
}
Expand Down
Loading