-
Notifications
You must be signed in to change notification settings - Fork 33
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주차 과제 STEP1 #54
base: kyleidea1
Are you sure you want to change the base?
Changes from all commits
04c907f
9f4d9a7
b2b33de
8eac791
a21a7c2
f5e9623
b3a1339
deb1a3c
d3adda3
b959286
a03fdc7
84ad89b
496dd1d
a62f21e
b043982
7c26fd4
e404119
61d5adc
62e3846
bbf77e9
9dd9533
f62e568
811b9bc
7499b1c
f898541
bb225cd
a5f25d9
cf5667f
0d7f0e6
81059af
ec020cd
e4f5aa3
bebe6de
e4839e6
d7d4f93
70486ba
a0be820
cde3ac6
4f8a9f6
43a339a
2fb4d24
6b912fa
7bcbe37
a5a9a26
2c28c03
7260c3e
0f1c076
2a4502e
f002a98
a30c46c
807dc2d
34cf47c
4594193
7c5707f
873df85
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,31 @@ | ||
# android-map-lacation STEP0 | ||
# android-map-location STEP1 | ||
|
||
## 개요 | ||
|
||
`android-map-search STEP2`의 작업 사항을 옮겨 온 브랜치입니다. | ||
`android-map-location STEP1`에서는 지도 화면과 검색 화면 간의 네비게이션을 추가하고, 검색 기능을 확장하여 저장된 검색어 목록을 관리합니다. | ||
|
||
## 기능 | ||
|
||
- **지도 화면 표시**: 앱을 처음 실행하면 지도 화면이 표시됩니다. | ||
- **검색 화면 이동**: 검색창을 선택하면 검색 화면으로 이동합니다. | ||
- **뒤로 가기로 지도 화면 복귀**: 검색 화면에서 뒤로 가기를 하면 지도 화면으로 돌아옵니다. | ||
- **검색 기능 확장**: 카카오로컬 API를 사용하여 검색어를 입력하면 검색 결과를 표시합니다. | ||
- 검색 결과는 15개 이상 표시됩니다. | ||
- 검색 결과 목록은 세로 스크롤이 가능합니다. | ||
- 검색어는 `X` 버튼을 눌러서 삭제할 수 있습니다. | ||
- **검색 결과 선택**: 검색 결과 목록에서 항목을 선택하면 선택된 항목이 검색어 저장 목록에 추가됩니다. | ||
- 저장된 검색어 목록은 가로 스크롤이 가능합니다. | ||
- 저장된 검색어는 `X` 버튼을 눌러서 삭제할 수 있습니다. | ||
- 저장된 검색어는 앱을 재실행해도 유지됩니다. | ||
|
||
## 기능 요구 사항 | ||
|
||
- 저장된 검색어를 선택하면 해당 검색어의 검색 결과가 표시됩니다. | ||
- 검색 결과 목록 중 하나의 항목을 선택하면 해당 항목의 위치를 지도에 표시합니다. | ||
- 앱 종료 시 마지막 위치를 저장하여 다시 앱 실행 시 해당 위치로 포커스합니다. | ||
- 카카오지도 onMapError() 호출 시 에러 화면을 보여줍니다. | ||
|
||
## 프로그래밍 요구 사항 | ||
|
||
- BottomSheet를 사용합니다. | ||
- 카카오 API 사용을 위한 앱 키를 외부에 노출하지 않습니다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package campus.tech.kakao.map | ||
|
||
import android.os.Bundle | ||
import android.widget.ImageView | ||
import android.widget.ProgressBar | ||
import androidx.appcompat.app.AppCompatActivity | ||
|
||
class AuthenticationErrorActivity : AppCompatActivity() { | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
setContentView(R.layout.activity_authentication_error) | ||
|
||
val retryIcon: ImageView = findViewById(R.id.retry_icon) | ||
val loadingIndicator: ProgressBar = findViewById(R.id.loading_indicator) | ||
|
||
retryIcon.setOnClickListener { | ||
loadingIndicator.visibility = ProgressBar.VISIBLE | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,127 @@ | ||
package campus.tech.kakao.map | ||
|
||
import android.content.Intent | ||
import android.graphics.Color | ||
import android.os.Bundle | ||
import android.util.Log | ||
import android.widget.LinearLayout | ||
import android.widget.TextView | ||
import androidx.appcompat.app.AppCompatActivity | ||
import com.google.android.material.bottomsheet.BottomSheetDialog | ||
import com.kakao.vectormap.KakaoMap | ||
import com.kakao.vectormap.KakaoMapReadyCallback | ||
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 mapView: MapView | ||
private lateinit var searchButton: LinearLayout | ||
private lateinit var bottomSheet: BottomSheetDialog | ||
|
||
private var name: String? = null | ||
private var address: String? = null | ||
private var kakaoMap: KakaoMap? = null | ||
private var latitude: String? = null | ||
private var longitude: String? = null | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
Log.d("here", "I'm in MapActivity") | ||
super.onCreate(savedInstanceState) | ||
setContentView(R.layout.activity_map) | ||
|
||
mapView = findViewById(R.id.map_view) | ||
searchButton = findViewById(R.id.search_button) | ||
val bottomSheetLayout = layoutInflater.inflate(R.layout.layout_map_bottom_sheet_dialog, null) | ||
bottomSheet = BottomSheetDialog(this) | ||
|
||
val defLat = "35.231627" | ||
val defLng = "129.084020" | ||
val lastLatLng = getSharedPreferences("lastLatLng", MODE_PRIVATE) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sharedPreference에선 double형을 읽고 쓸수가 없어서 string type으로 관리하셨군요 |
||
latitude = lastLatLng.getString("lastLat", defLat) // 기본 위치: 부산대. | ||
longitude = lastLatLng.getString("lastLng", defLng) // 기본 위치: 부산대. | ||
Log.d("sharedPreference", "saveLatLng밖에꺼: $latitude") | ||
Log.d("sharedPreference", "saveLatLng밖에꺼: $longitude") | ||
|
||
// Intent에서 값을 가져옴 | ||
intent?.let { | ||
latitude = it.getStringExtra("mapY") ?: latitude | ||
longitude = it.getStringExtra("mapX") ?: longitude | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. intent extra에 들어가는 key값들은 보내는쪽과 받는쪽 둘다 사용하기에 상수로 관리하시면 좋습니다 |
||
name = it.getStringExtra("name") | ||
address = it.getStringExtra("address") | ||
} | ||
|
||
Log.d("latlngcheck1", "Intent에서 가져온 값: $latitude, $longitude, $name") | ||
|
||
mapView.start( | ||
object : MapLifeCycleCallback() { | ||
override fun onMapDestroy() {} | ||
override fun onMapError(error: Exception) {} | ||
override fun onMapDestroy() { | ||
} | ||
|
||
override fun onMapError(error: Exception) { | ||
Log.e("MapActivity", "Map error: ${error.message}") | ||
val errorIntent = Intent(this@MapActivity, AuthenticationErrorActivity::class.java) | ||
startActivity(errorIntent) | ||
finish() | ||
} | ||
|
||
}, | ||
object : KakaoMapReadyCallback() { | ||
override fun onMapReady(kakaoMap: KakaoMap) {} | ||
override fun onMapReady(kakaoMap: KakaoMap) { | ||
Log.d("latlngcheck2", "onMapReady에서: $latitude, $longitude") | ||
[email protected] = kakaoMap | ||
name?.let { | ||
addMarker(kakaoMap, latitude ?: defLat, longitude ?: defLng, it) | ||
saveLatLng(latitude ?: defLat, longitude ?: defLng) // 마커 찍은 위치를 sharedPreference에 저장 | ||
val tvBottomSheetPlaceName = bottomSheetLayout.findViewById<TextView>(R.id.bottomSheetPlaceName) | ||
val tvBottomSheetPlaceAddress = bottomSheetLayout.findViewById<TextView>(R.id.bottomsheetPlaceAddress) | ||
tvBottomSheetPlaceName.text = name | ||
tvBottomSheetPlaceAddress.text = address | ||
bottomSheet.setContentView(bottomSheetLayout) | ||
bottomSheet.show() | ||
} | ||
} | ||
|
||
override fun getPosition(): LatLng { | ||
Log.d("latlngcheck3", "getPosition에서: $latitude, $longitude") | ||
val lat = (latitude ?: defLat).toDouble() | ||
val lng = (longitude ?: defLng).toDouble() | ||
return LatLng.from(lat, lng) | ||
} | ||
} | ||
) | ||
|
||
searchButton.setOnClickListener { | ||
val intent = Intent(this@MapActivity, SearchActivity::class.java) | ||
startActivity(intent) | ||
val mapToSearchIntent = Intent(this@MapActivity, SearchActivity::class.java) | ||
startActivity(mapToSearchIntent) | ||
finish() | ||
} | ||
} | ||
|
||
private fun addMarker(kakaoMap: KakaoMap, latitude: String, longitude: String, name: String) { | ||
Log.d("addMarker", "addMarker: $latitude, $longitude, $name") | ||
val styles = kakaoMap.labelManager?.addLabelStyles(LabelStyles.from(LabelStyle.from(R.drawable.location_marker).setTextStyles(25, Color.BLACK))) | ||
val options = LabelOptions.from(LatLng.from(latitude.toDouble(), longitude.toDouble())).setStyles(styles) | ||
val layer = kakaoMap.labelManager?.layer | ||
val label = layer?.addLabel(options) | ||
label?.changeText(name) | ||
} | ||
|
||
private fun saveLatLng(latitude: String, longitude: String) { | ||
val sharedPreference = getSharedPreferences("lastLatLng", MODE_PRIVATE) | ||
sharedPreference.edit().apply { | ||
putString("lastLat", latitude) | ||
putString("lastLng", longitude) | ||
apply() | ||
} | ||
Log.d("sharedPreference", "saveLatLng: ${sharedPreference.getString("lastLat", "default_lat")}") | ||
Log.d("sharedPreference", "saveLatLng: ${sharedPreference.getString("lastLng", "default_lng")}") | ||
} | ||
|
||
override fun onResume() { | ||
super.onResume() | ||
mapView.resume() | ||
|
@@ -46,4 +131,5 @@ class MapActivity : AppCompatActivity() { | |
super.onPause() | ||
mapView.pause() | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<shape xmlns:android="http://schemas.android.com/apk/res/android"> | ||
|
||
<solid | ||
android:color="@color/white"/> | ||
|
||
<padding | ||
android:left="10dp" | ||
android:right="10dp" | ||
android:top="10dp" | ||
android:bottom="10dp" /> | ||
|
||
</shape> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<?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=".AuthenticationErrorActivity"> | ||
|
||
<androidx.constraintlayout.widget.ConstraintLayout | ||
android:layout_width="wrap_content" | ||
android:layout_height="wrap_content" | ||
app:layout_constraintTop_toTopOf="parent" | ||
app:layout_constraintBottom_toBottomOf="parent" | ||
app:layout_constraintLeft_toLeftOf="parent" | ||
app:layout_constraintRight_toRightOf="parent"> | ||
<TextView | ||
android:id="@+id/error_message" | ||
android:layout_width="wrap_content" | ||
android:layout_height="wrap_content" | ||
android:text="지도 인증을 실패 했습니다.\n다시 시도해 주세요." | ||
android:textSize="16sp" | ||
android:gravity="center" | ||
android:padding="16dp" | ||
app:layout_constraintTop_toTopOf="parent" | ||
app:layout_constraintBottom_toTopOf="@id/error_details" | ||
app:layout_constraintStart_toStartOf="parent" | ||
app:layout_constraintEnd_toEndOf="parent" /> | ||
|
||
<TextView | ||
android:id="@+id/error_details" | ||
android:layout_width="wrap_content" | ||
android:layout_height="wrap_content" | ||
android:text="MapAuthException(401): Unauthorized" | ||
android:textSize="14sp" | ||
android:gravity="center" | ||
android:padding="8dp" | ||
app:layout_constraintTop_toBottomOf="@id/error_message" | ||
app:layout_constraintStart_toStartOf="parent" | ||
app:layout_constraintEnd_toEndOf="parent" /> | ||
|
||
<ImageView | ||
android:id="@+id/retry_icon" | ||
android:layout_width="48dp" | ||
android:layout_height="48dp" | ||
app:srcCompat="@drawable/ic_retry" | ||
android:padding="16dp" | ||
app:layout_constraintTop_toBottomOf="@id/error_details" | ||
app:layout_constraintStart_toStartOf="parent" | ||
app:layout_constraintEnd_toEndOf="parent" /> | ||
|
||
<ProgressBar | ||
android:id="@+id/loading_indicator" | ||
android:layout_width="wrap_content" | ||
android:layout_height="wrap_content" | ||
app:layout_constraintTop_toBottomOf="@id/retry_icon" | ||
app:layout_constraintStart_toStartOf="parent" | ||
app:layout_constraintEnd_toEndOf="parent" | ||
android:indeterminate="true"/> | ||
</androidx.constraintlayout.widget.ConstraintLayout> | ||
</androidx.constraintlayout.widget.ConstraintLayout> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
xmlns:app="http://schemas.android.com/apk/res-auto" | ||
android:id="@+id/bottom_sheet_layout" | ||
android:layout_width="match_parent" | ||
android:layout_height="300dp" | ||
android:background="@drawable/bottom_sheet_background" | ||
app:behavior_hideable="false" | ||
app:behavior_peekHeight="150dp" | ||
android:orientation="vertical" | ||
android:padding="10dp" | ||
android:clickable="false" | ||
android:focusable="false" | ||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> | ||
|
||
<androidx.constraintlayout.widget.ConstraintLayout | ||
android:layout_width="match_parent" | ||
android:layout_height="match_parent"> | ||
|
||
<TextView | ||
android:id="@+id/bottomSheetPlaceName" | ||
android:layout_width="wrap_content" | ||
android:layout_height="wrap_content" | ||
android:text="부산대학교" | ||
android:textSize="20sp" | ||
android:textStyle="bold" | ||
app:layout_constraintTop_toTopOf="parent" | ||
app:layout_constraintStart_toStartOf="parent" /> | ||
|
||
<TextView | ||
android:id="@+id/bottomsheetPlaceAddress" | ||
android:layout_width="wrap_content" | ||
android:layout_height="wrap_content" | ||
android:text="부산 금정구 장전동 40" | ||
android:textSize="16sp" | ||
app:layout_constraintTop_toBottomOf="@id/bottomSheetPlaceName" | ||
app:layout_constraintStart_toStartOf="parent" /> | ||
|
||
</androidx.constraintlayout.widget.ConstraintLayout> | ||
|
||
</LinearLayout> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
미리 셋팅해야할게 있는게 아니라면
bottomSheet가 필요할때 객체를 생성하는건 어떨까요?