[Android] 방탈출 카페 찾기 App 만들기

Jinny·2021년 5월 13일
6

Project - Kotlin

목록 보기
11/11

🚩 방탈출 카페 찾기 App

오늘은 원하는 지역의 방탈출 카페 목록을 지도에 보여주는 App을 만든다!

온라인 예약이 가능한 카페의 경우 '예약하기' 버튼을 통해 예약페이지로 이동할 수 있다.

또한, 카페의 정보를 친구들에게 공유해줄 수 도 있다!

🚩 사용 기술 및 도구

언어 : Kotlin (100%)
환경 : Android Studio

사용 기술

  • Naver Map API
  • ViewPager2
  • FrameLayout
  • CoordinatorLayout
  • BottomSheetBehavior
  • Retrofit
  • Glide

🚩 기능 살펴보기

🔸 지역명에 따라 방탈출카페 data를 dataClass에 담기

API를 통해 데이터 통신을 하기 위해 Retrofit을 사용하였다.
Retrofit 사용법은 지난 포스팅에 정리해놓았다.

◾ 인터페이스 추가 - RoomsSerivece

interface RoomsService {
    @GET(api 나머지 주소를 적어주세용)
    fun getRoomsList(
        @Query("query") query: String,
        @Query("displayCount") displayCount:Int = 20
    ): Call<RoomsResponse>
}

◾ 데이터 클래스 추가 - RoomsModel

data class RoomsModel(
    @SerializedName("id")
    val id: Int,
    @SerializedName("name")
    val title: String,
    @SerializedName("menuInfo")
    val price: String,
    @SerializedName("thumUrl")
    val imgUrl: String,
    @SerializedName("x")
    val lng: Double,
    @SerializedName("y")
    val lat: Double,
    @SerializedName("tel")
    val tel: String,
    @SerializedName("abbrAddress")
    val address: String,
    @SerializedName("bizhourInfo")
    val hourInfo: String,
    @SerializedName("hasNaverBooking")
    val hasBooking: Boolean,
    @SerializedName("naverBookingUrl")
    val bookingUrl: String
) : Serializable
// Serializable을 import 하여 직렬화를 해주어야 한다.

◾ items라는 리스트로 묶었으니 DTO 추가 - data class
내가 받아오는 데이터는 3중으로 구성되어있다.

data class RoomsResponse(
    @SerializedName("result")
    var roomsResult: RoomsResult
)
data class RoomsResult(
    @SerializedName("place")
    var place : RoomsList
)
data class RoomsList(
    @SerializedName("list")
    // 이렇게 마지막 list에 담긴 아이템들은 RoomsModel 타입의 리스트로 저장한다. 
    val items: List<RoomsModel> = arrayListOf<RoomsModel>()
)

◾ 레트로핏 객체 생성 후 데이터 가져오기

// retrofit 구현
val retrofit = Retrofit.Builder().baseUrl(api 기본주소를 적어주세요)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            
        // retrofit 생성
        retrofit.create(RoomsService::class.java).also {

            // 또한, 바로 생성한 인터페이스의 getRoomsList()를 실행한다
            it.getRoomsList(query = (searchWord.replace(" ","")  + " 방탈출카페"))
                .enqueue(object : Callback<RoomsResponse> {
                    override fun onResponse(
                        call: Call<RoomsResponse>,
                        response: Response<RoomsResponse>
                    ) {
                        if (response.isSuccessful.not()) {
                            // 실패 할 경우
                            binding!!.progressbar.visibility = View.INVISIBLE
                            Toast.makeText(
                                this@MainActivity,
                                "에러! 잠시 후 다시 시도해주세요.",
                                Toast.LENGTH_SHORT
                            ).show()
                            return
                        }
                        // 통신 성공 시
                        response.body()?.let { dto ->
                            binding!!.progressbar.visibility = View.INVISIBLE
                            if (dto.roomsResult.place == null) {
                                binding!!.textField.error = "검색 결과가 없습니다. 또는 지역명을 확인해주세요."
                                return
                            }
                            // 데이터를 가지고 결과를 보여줄 Activity로 이동
                            val datalist = dto.roomsResult.place.items
                            val intent = Intent(this@MainActivity, ResultActivity::class.java)
                            val list: ArrayList<RoomsModel> = ArrayList<RoomsModel>(datalist)
                            intent.putExtra("datalist", list)
                            startActivity(intent)

                        }
                    }

                    override fun onFailure(call: Call<RoomsResponse>, t: Throwable) {
			// 통신 실패 시
                    }
		})
}

🔸 NaverMap API 연결하기

◾ api 신청 및 clientId 추가하기
api 사용신청은 네이버 API 사이트에서 신청하면 된다.
신청 완료 후 부여받은 clientId는 따로 values로 저장 후 meta-data에 추가해주어야한다.

// api_key.xml
<resources>
    <string name="naver_map_Client_Id">부여받은 ClientID</string>
</resources>

// Manifest.xml
<meta-data
            android:name="com.naver.maps.map.CLIENT_ID"
            android:value="@string/naver_map_Client_Id" />

◾ 프로젝트에 Naver Map 추가하기

// build.gradle
allprojects {
    repositories {
        google()
        jcenter()
        maven("https://naver.jfrog.io/artifactory/maven/")
    }
}

dependencies {
    // 네이버 지도 SDK
    implementation("com.naver.maps:map-sdk:3.11.0")
}

◾ layout에 mapView 추가

<com.naver.maps.map.MapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

🔸 Data의 방탈출 카페들을 지도에 마킹하기

이제 MainActivity에서 넘겨받은 데이터 list를 가지고 지도에 마킹을 해준다.

// 네이버 지도 준비하기
override fun onMapReady(map: NaverMap) {
        naverMap = map

        // 확대/축소 정도 조절!
        naverMap.maxZoom = 18.0
        naverMap.minZoom = 10.0

        val uiSetting = naverMap.uiSettings
        
        // true로 하면 기본 버튼
        // 현재 위치 버튼
        uiSetting.isLocationButtonEnabled = false
        uiSetting.setLogoMargin(30, 0, 0, 230)
        // 내가 만든 버튼으로 변경
        currentLocationButton.map = naverMap

        locationSource = FusedLocationSource(this, LOCATION_PERMISSION_REQUEST_CODE)
        naverMap.locationSource = locationSource

        setListWithdata(datalist)
    }
	
    //  데이터를 가지고 list를 넘겨주어라!
    private fun setListWithdata(datalist: ArrayList<RoomsModel>) {
        updateMarker(datalist)
        viewPagerAdpater.submitList(datalist)
        recyclerViewAdapter.submitList(datalist)
        bottomSheetTextView.text = "${datalist.size}개의 결과보기"
    }

    // 받은 list 하나씩 지도에 마크 찍기
    private fun updateMarker(rooms: List<RoomsModel>) {
        rooms.forEach { room ->
            val marker = Marker()
            marker.position = LatLng(room.lat, room.lng)
            marker.onClickListener = this
            marker.map = naverMap
            marker.tag = room.id
            marker.icon = MarkerIcons.RED
        }
    }

🔸 CardView와 지도 마커 연결하기

// viewPager(CardView로 생성했음)를 넘길 때 마다
// 해당하는 item의 마커로 지도 이동
viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
            override fun onPageSelected(position: Int) {
                super.onPageSelected(position)

                val selectedRoomModel = viewPagerAdpater.currentList[position]
                val cameraUpdate =
                    CameraUpdate.scrollTo(LatLng(selectedRoomModel.lat, selectedRoomModel.lng))
                        .animate(CameraAnimation.Easing)

                naverMap.moveCamera(cameraUpdate)
            }
        })
        
// 지도 위 마커 클릭 시 해당하는 item의 viewPager로 이동 
override fun onClick(overlay: Overlay): Boolean {
        val selectedModel = viewPagerAdpater.currentList.firstOrNull() {
            it.id == overlay.tag
        }
        selectedModel?.let {
            val position = viewPagerAdpater.currentList.indexOf(it)
            viewPager.currentItem = position
        }
        return true
    }

🔸 장소 정보 보여주기 (+ 공유하기, 예약하기 )

viewPager의 아이템을 클릭 하거나, 하단 뷰의 아이템 클릭 시
장소 정보 Activity로 이동한다.

// 해당 장소의 데이터를 가지고 Activity 이동
private fun goDetailActivity(roomsModel: RoomsModel) {
        val intent = Intent(this, DetailActivity::class.java)
        intent.putExtra("data", roomsModel)
        startActivity(intent)
}

// 공유하기 기능을 통해 장소 정보를 외부 앱으로 전달할 수 있음
private fun shareInfo(data: RoomsModel) {
        val intent = Intent().apply {
            action = Intent.ACTION_SEND
            putExtra(
                Intent.EXTRA_TEXT,
                "[방탈출 고고씽!\uD83C\uDFC3] 지금 \"${data.title}\"를 확인해보세요.\n\uD83D\uDC49 https://m.place.naver.com/place/${data.id}  "
            )
            type = "text/plain"
        }
        startActivity(Intent.createChooser(intent, null))
}

// 네이버 예약하기가 가능한 장소의 경우 예약페이지로 이동할 수 있음
private fun goBookingSite(dataUri: String) {
        val intent = Intent(Intent.ACTION_VIEW)
        intent.data = Uri.parse(dataUri)
        startActivity(intent)
}

🚩 완성!


관련 문서 : NAVER MAP 안드로이드 개발가이드
참고했던 블로그 : mechacat님 tistory
profile
신입 개발자👩‍💻

2개의 댓글

comment-user-thumbnail
2022년 5월 18일

방탈출 카페 정보는 api가 따로 있나요??

1개의 답글