네이버 맵 api를 이용해 지도를 메인으로 숙소 목록을 서버에서 받아와 마커로 나타내고 하단에 viewPager를 이용해 좌우로 스크롤이 가능한 페이저를 두어 숙소를 살필 수 있다.
완성 화면
주요 기능
사용 기술
build.gradle
implementation 'com.naver.maps:map-sdk:3.11.0'
implementation 'com.google.android.gms:play-services-location:18.0.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.github.bumptech.glide:glide:4.12.0'
settings.gradle
repositories {
maven {
url 'https://naver.jfrog.io/artifactory/maven/'
}
}
layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.naver.maps.map.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="80dp" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/houseViewPager"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_gravity="bottom"
android:layout_marginBottom="120dp"
android:orientation="horizontal" />
<com.naver.maps.map.widget.LocationButtonView
android:id="@+id/currentLocationButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|start"
android:layout_margin="12dp" />
<include layout="@layout/bottom_sheet" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
layout/bottom_sheet.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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/top_radius_white_background"
app:behavior_peekHeight="100dp"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<View
android:layout_width="30dp"
android:layout_height="3dp"
android:layout_marginTop="12dp"
android:background="#cccccc"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/bottomSheetTitleTextView"
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_marginTop="16dp"
android:gravity="center"
android:text="여러개의 숙소"
android:textColor="@color/black"
android:textSize="15sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/lineView"
android:layout_width="0dp"
android:layout_height="1dp"
android:background="#cccccc"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/bottomSheetTitleTextView" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/lineView"
app:layout_constraintVertical_bias="1.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
layout_behavior, behavior_peekHeight 속성을 이용해 아래에서 위로 스크롤하여 당기면 100dp만큼 올라올 수 있도록 해주었다.
layout/item_house.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="wrap_content">
<ImageView
android:id="@+id/thumbnailImageView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="24dp"
app:layout_constraintDimensionRatio="3:2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/titleTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="24dp"
android:textColor="@color/black"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/thumbnailImageView"
tools:text="강남역 도보 5분!! 쾌적한 환경!!" />
<TextView
android:id="@+id/priceTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="24dp"
android:textColor="@color/black"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/titleTextView"
tools:text="24,000원" />
</androidx.constraintlayout.widget.ConstraintLayout>
layout/item_house_detail_viewpager.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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"
android:layout_marginStart="30dp"
android:layout_marginEnd="30dp"
android:background="@color/white"
app:cardCornerRadius="16dp"
tools:layout_height="100dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/thumbnailImageView"
android:layout_width="100dp"
android:layout_height="100dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/titleTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="12dp"
android:maxLines="2"
android:textColor="@color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/thumbnailImageView"
app:layout_constraintTop_toTopOf="parent"
tools:text="강남역 도보 5분!! 쾌적한 환경!!강남역 도보 5분!! 쾌적한 환경!!강남역 도보 5분!! 쾌적한 환경!!강남역 도보 5분!! 쾌적한 환경!!강남역 도보 5분!! 쾌적한 환경!!강남역 도보 5분!! 쾌적한 환경!!" />
<TextView
android:id="@+id/priceTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:maxLines="1"
android:textColor="@color/black"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/thumbnailImageView"
app:layout_constraintTop_toBottomOf="@id/titleTextView"
tools:text="23,000원" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
drawable/top_radius_white_background.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white" />
<corners
android:topLeftRadius="30dp"
android:topRightRadius="30dp" />
</shape>
valuse/api_key.xml
<resources>
<string name="naver_map_client_id">client_id</string>
</resources>
앞서 만든 application 에서 인증정보/Client ID 을 작성해준다.
Manifest
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET"/>
<meta-data
android:name="com.naver.maps.map.CLIENT_ID"
android:value="@string/naver_map_client_id"/>
MainActivity
class MainActivity : AppCompatActivity(), OnMapReadyCallback {
companion object {
private const val LOCATION_PERMISSION_REQUEST_CODE = 1000
}
private val mapView: MapView by lazy {
findViewById<MapView>(R.id.mapView)
}
private lateinit var locationSource: FusedLocationSource
private lateinit var naverMap: NaverMap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mapView.onCreate(savedInstanceState)
mapView.getMapAsync(this)
}
override fun onMapReady(map: NaverMap) {
naverMap = map
// 줌 범위 설정
naverMap.maxZoom = 10.0
naverMap.minZoom = 10.0
// 현재 보여지는 지도 위치
val cameraUpdate = CameraUpdate.scrollTo(LatLng(37.497885, 127.027512))
naverMap.moveCamera(cameraUpdate)
// 현재 위치 버튼
val uiSetting = naverMap.uiSettings
uiSetting.isLocationButtonEnabled = false
currentLocationButton.map = naverMap
// 위치 권한 요청
locationSource = FusedLocationSource(this@MainActivity, LOCATION_PERMISSION_REQUEST_CODE)
naverMap.locationSource = locationSource
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) {
return
}
if (locationSource.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
if (!locationSource.isActivated) {
// 권한 설정 거부시 위치 추적 사용하지 않음
naverMap.locationTrackingMode = LocationTrackingMode.None
}
return
}
}
private val currentLocationButton: LocationButtonView by lazy {
findViewById(R.id.currentLocationButton)
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onRestart() {
super.onRestart()
mapView.onResume()
}
override fun onPause() {
super.onPause()
mapView.onPause()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
mapView.onSaveInstanceState(outState)
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
override fun onLowMemory() { // 메모리가 별로 없을 때 호출되는 함수
super.onLowMemory()
mapView.onLowMemory()
}
}
mocky를 사용해 작성해둔 json 파일을 서버에서 받아올 수 있도록 하였다. 이후 retorfit을 통해 받아와 처리해주었다.
HouseService
interface HouseService {
@GET("/v3/511c37d3-79c1-455f-9efb-98b5d594e640")
fun getHouseList(): Call<HouseDto>
}
HouseDto
data class HouseDto(
val items: List<HouseModel>
)
HouseModel
data class HouseModel(
val id: Int,
val title: String,
val price: String,
val lat: Double,
val lng: Double,
val imgUrl: String
)
MainActivity
private fun getHouseListFromAPI() {
val retrofit = Retrofit.Builder()
.baseUrl("https://run.mocky.io")
.addConverterFactory(GsonConverterFactory.create())
.build()
retrofit.create(HouseService::class.java).also {
it.getHouseList()
.enqueue(object : Callback<HouseDto> {
@SuppressLint("SetTextI18n")
override fun onResponse(call: Call<HouseDto>, response: Response<HouseDto>) {
if (response.isSuccessful.not()) {
// 실패 처리에 대한 구현
return
}
response.body()?.let { dto ->
// TODO 성공한 경우 마커 추가, viewPager, recyclerView 뿌려주기
response.body()?.let { dto ->
updateMarker(dto.items)
viewPagerAdapter.submitList(dto.items)
recyclerAdapter.submitList(dto.items) // 새 리스트로 갱신
bottomSheetTitleTextView.text = "${dto.items.size}개의 숙소"
}
}
override fun onFailure(call: Call<HouseDto>, t: Throwable) {
// 실패 처리에 대한 구현
}
})
}
}
응답을 성공적으로 받아온 경우 바디를 검사해 dto로 받아와 처리해주고, 추후 HouseModel리스트를 받아와 마커를 추가하고 viewPager, recyclerView에 각각 뿌려주도록 할 것이다. (코드는 미리 작성)
MainActivity
private fun updateMarker(houses: List<HouseModel>) {
houses.forEach { house ->
val marker = Marker()
marker.position = LatLng(house.lat, house.lng)
marker.map = naverMap
marker.tag = house.id
marker.icon = MarkerIcons.BLACK
marker.iconTintColor = Color.RED
}
}
HouseViewPagerAdapter
class HouseViewPagerAdapter :
ListAdapter<HouseModel, HouseViewPagerAdapter.ItemViewHolder>(differ) {
inner class ItemViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
fun bind(houseModel: HouseModel) {
val titleTextView = view.findViewById<TextView>(R.id.titleTextView)
val priceTextView = view.findViewById<TextView>(R.id.priceTextView)
val thumbnailImageView = view.findViewById<ImageView>(R.id.thumbnailImageView)
titleTextView.text = houseModel.title
priceTextView.text = houseModel.price
Glide
.with(thumbnailImageView.context)
.load(houseModel.imgUrl)
.into(thumbnailImageView)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val inflater = LayoutInflater.from(parent.context)
return ItemViewHolder(inflater.inflate(R.layout.item_house_detail_viewpager, parent, false))
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
holder.bind(currentList[position])
}
companion object {
val differ = object : DiffUtil.ItemCallback<HouseModel>() {
override fun areItemsTheSame(oldItem: HouseModel, newItem: HouseModel): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: HouseModel, newItem: HouseModel): Boolean {
return oldItem == newItem
}
}
}
}
MainActivity
private val viewPager: ViewPager2 by lazy {
findViewById(R.id.houseViewPager)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mapView.onCreate(savedInstanceState)
viewPager.adapter = viewPagerAdapter
}
HouseListAdapter
class HouseListAdapter : ListAdapter<HouseModel, HouseListAdapter.ItemViewHolder>(differ) {
inner class ItemViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
fun bind(houseModel: HouseModel) {
val titleTextView = view.findViewById<TextView>(R.id.titleTextView)
val priceTextView = view.findViewById<TextView>(R.id.priceTextView)
val thumbnailImageView = view.findViewById<ImageView>(R.id.thumbnailImageView)
titleTextView.text = houseModel.title
priceTextView.text = houseModel.price
Glide
.with(thumbnailImageView.context)
.load(houseModel.imgUrl)
// transeform()을 통해 이미지를 변형시키고, RoundCorners를 통해 모서리를 둥글게 바꿔줌
.transform(CenterCrop(), RoundedCorners(dpToPx(thumbnailImageView.context, 12)))
.into(thumbnailImageView)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val inflater = LayoutInflater.from(parent.context)
return ItemViewHolder(inflater.inflate(R.layout.item_house, parent, false))
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
holder.bind(currentList[position])
}
// roundCorners는 픽셀 단위로 설정해줘야 하기 때문에 dbToPx()를 이용해 dp를 px로 변환
private fun dpToPx(context: Context, dp: Int): Int {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), context.resources.displayMetrics).toInt()
}
companion object {
val differ = object : DiffUtil.ItemCallback<HouseModel>() {
override fun areItemsTheSame(oldItem: HouseModel, newItem: HouseModel): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: HouseModel, newItem: HouseModel): Boolean {
return oldItem == newItem
}
}
}
}
200*200 이미지를 가져왔기 때문에 목록에서 보일 때 정사각형으로 뜬다. 2:3 크기로 보여주기 위해 glide에서 설정 값을 추가해주었다.
MainActivity
viewPager.adapter = viewPagerAdapter
recyclerView.adapter = recyclerAdapter
recyclerView.layoutManager = LinearLayoutManager(this)
bottom_sheet.xml에 작성해둔 recyclerView를 가져와 onCreate()에 뷰를 연결시켜 주었다.
MainActivity
viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
val selectedHouseModel = viewPagerAdapter.currentList[position]
val cameraUpdate =
CameraUpdate.scrollTo(LatLng(selectedHouseModel.lat, selectedHouseModel.lng))
.animate(CameraAnimation.Easing)
naverMap.moveCamera(cameraUpdate)
}
})
private fun updateMarker(houses: List<HouseModel>) {
houses.forEach { house ->
marker.onClickListener = this
}
}
viewPager와 마커를 연결시켜 해당 viewPager를 클릭했을 때 마커가 있는 곳으로 카메라가 이동하도록 구현해주었다.
MainActivity
class MainActivity : AppCompatActivity(), OnMapReadyCallback, Overlay.OnClickListener {
override fun onClick(overly: Overlay): Boolean {
// overlay: 마커의 총 집합
// tag: id 값
// firstOrNull: 먼저 나오는 아이템 반환, 없으면 null 반환
val selectedModel = viewPagerAdapter.currentList.firstOrNull {
it.id == overly.tag
}
selectedModel?.let {
// 널이 아닌 경우 포지션을 찾았다면 포지션의 위치 이동
val position = viewPagerAdapter.currentList.indexOf(it)
viewPager.currentItem = position
}
return true
}
}
viewPager와 마커를 연결시켜 마커를 클릭했을 때 해당 위치의 viewPager가 뜰 수 있도록 구현해주었다.
마커를 클릭하면 viewPager의 리스트에서 해당 아이디를 갖는 숙소를 찾은 뒤 해당 숙소의 현재 아이템 포지션으로 지정해도록 하였다.
HouseViewPagerAdapter
class HouseViewPagerAdapter(val itemClicked: (HouseModel) -> Unit) :
ListAdapter<HouseModel, HouseViewPagerAdapter.ItemViewHolder>(differ) {
inner class ItemViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
fun bind(houseModel: HouseModel) {
view.setOnClickListener {
itemClicked(houseModel)
}
}
}
}
MainActivity
private val viewPagerAdapter = HouseViewPagerAdapter(itemClicked = {
val intent = Intent()
.apply {
action = Intent.ACTION_SEND
putExtra(
Intent.EXTRA_TEXT,
"[지금 이 가격에 예약하세요!!] ${it.title} ${it.price} 사진보기 : ${it.imgUrl}"
)
type = "text/plain"
}
startActivity(Intent.createChooser(intent, null))
})
viewPager의 항목이 클릭된 경우 chooser를 통해 다른 앱으로 공유할 수 있도록 해주었고, 클릭리스너를 달아 putExtra로 설정된 텍스트를 공유할 수 있도록 하였다.
HouseViewPagerAdapter
class HouseViewPagerAdapter(val itemClicked: (HouseModel) -> Unit) :
ListAdapter<HouseModel, HouseViewPagerAdapter.ItemViewHolder>(differ) {
inner class ItemViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
fun bind(houseModel: HouseModel) {
val titleTextView = view.findViewById<TextView>(R.id.titleTextView)
val priceTextView = view.findViewById<TextView>(R.id.priceTextView)
val thumbnailImageView = view.findViewById<ImageView>(R.id.thumbnailImageView)
titleTextView.text = houseModel.title
priceTextView.text = houseModel.price
view.setOnClickListener {
itemClicked(houseModel)
}
Glide
.with(thumbnailImageView.context)
.load(houseModel.imgUrl)
.into(thumbnailImageView)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val inflater = LayoutInflater.from(parent.context)
return ItemViewHolder(inflater.inflate(R.layout.item_house_detail_viewpager, parent, false))
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
holder.bind(currentList[position])
}
companion object {
val differ = object : DiffUtil.ItemCallback<HouseModel>() {
override fun areItemsTheSame(oldItem: HouseModel, newItem: HouseModel): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: HouseModel, newItem: HouseModel): Boolean {
return oldItem == newItem
}
}
}
}
MainActivity
class MainActivity : AppCompatActivity(), OnMapReadyCallback, Overlay.OnClickListener {
companion object {
private const val LOCATION_PERMISSION_REQUEST_CODE = 1000
}
private lateinit var naverMap: NaverMap
private lateinit var locationSource: FusedLocationSource
private val viewPager: ViewPager2 by lazy {
findViewById(R.id.houseViewPager)
}
private val mapView: MapView by lazy {
findViewById(R.id.mapView)
}
private val recyclerView: RecyclerView by lazy {
findViewById(R.id.recyclerView)
}
private val currentLocationButton: LocationButtonView by lazy {
findViewById(R.id.currentLocationButton)
}
private val bottomSheetTitleTextView: TextView by lazy {
findViewById(R.id.bottomSheetTitleTextView)
}
private val viewPagerAdapter = HouseViewPagerAdapter(itemClicked = {
val intent = Intent()
.apply {
action = Intent.ACTION_SEND
putExtra(
Intent.EXTRA_TEXT,
"[지금 이 가격에 예약하세요!!] ${it.title} ${it.price} 사진보기 : ${it.imgUrl}"
)
type = "text/plain"
}
startActivity(Intent.createChooser(intent, null))
})
private val recyclerAdapter = HouseListAdapter()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mapView.onCreate(savedInstanceState)
mapView.getMapAsync(this)
viewPager.adapter = viewPagerAdapter
recyclerView.adapter = recyclerAdapter
recyclerView.layoutManager = LinearLayoutManager(this)
viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
val selectedHouseModel = viewPagerAdapter.currentList[position]
val cameraUpdate =
CameraUpdate.scrollTo(LatLng(selectedHouseModel.lat, selectedHouseModel.lng))
.animate(CameraAnimation.Easing)
naverMap.moveCamera(cameraUpdate)
}
})
}
override fun onMapReady(map: NaverMap) {
naverMap = map
naverMap.maxZoom = 18.0
naverMap.minZoom = 10.0
val cameraUpdate = CameraUpdate.scrollTo(LatLng(37.497885, 127.027512))
naverMap.moveCamera(cameraUpdate)
val uiSetting = naverMap.uiSettings
uiSetting.isLocationButtonEnabled = false
currentLocationButton.map = naverMap
locationSource = FusedLocationSource(this@MainActivity, LOCATION_PERMISSION_REQUEST_CODE)
naverMap.locationSource = locationSource
getHouseListFromAPI()
}
private fun getHouseListFromAPI() {
val retrofit = Retrofit.Builder()
.baseUrl("https://run.mocky.io")
.addConverterFactory(GsonConverterFactory.create())
.build()
retrofit.create(HouseService::class.java).also {
it.getHouseList()
.enqueue(object : Callback<HouseDto> {
@SuppressLint("SetTextI18n")
override fun onResponse(call: Call<HouseDto>, response: Response<HouseDto>) {
if (response.isSuccessful.not()) {
// 실패 처리에 대한 구현
return
}
response.body()?.let { dto ->
updateMarker(dto.items)
viewPagerAdapter.submitList(dto.items)
recyclerAdapter.submitList(dto.items) // 새 리스트로 갱신
bottomSheetTitleTextView.text = "${dto.items.size}개의 숙소"
}
}
override fun onFailure(call: Call<HouseDto>, t: Throwable) {
// 실패 처리에 대한 구현
}
})
}
}
private fun updateMarker(houses: List<HouseModel>) {
houses.forEach { house ->
val marker = Marker()
marker.position = LatLng(house.lat, house.lng)
marker.onClickListener = this
marker.map = naverMap
marker.tag = house.id
marker.icon = MarkerIcons.BLACK
marker.iconTintColor = Color.RED
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) {
return
}
if (locationSource.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
if (!locationSource.isActivated) {
naverMap.locationTrackingMode = LocationTrackingMode.None
}
return
}
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onResume() {
super.onResume()
mapView.onResume()
}
override fun onPause() {
super.onPause()
mapView.onPause()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
mapView.onSaveInstanceState(outState)
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
override fun onLowMemory() {
super.onLowMemory()
mapView.onLowMemory()
}
override fun onClick(overly: Overlay): Boolean {
val selectedModel = viewPagerAdapter.currentList.firstOrNull {
it.id == overly.tag
}
selectedModel?.let {
val position = viewPagerAdapter.currentList.indexOf(it)
viewPager.currentItem = position
}
return true
}
}