aop-part3-chapter07 - 에어비엔비
목차
- 인트로 (완성앱 & 구현 기능 소개)
- 네이버 지도 API 사용하기
- 우리집 위치에 마커 찍어보기
- 지도 위에 BottomSheetDialog 띄우기
- Retrofit을 사용하여 서버에서 가져온 예약가능 목록 보여주기
- 지도 위에 예약가능 집 목록 띄우기
- BottomSheetDialog에 예약가능 집 목록 띄우기
- 마커와 리스트 연동하기
- 공유하기 기능 구현하기
- 마무리
결과화면


이 챕터를 통해 배우는 것
- Naver Map API 사용하기
- ViewPager2 사용하기
- FrameLayout 알아보기
- CoordinatorLayout 사용하기
- BottomSheetBehavior 사용하기
- Retrofit 사용하기
- Glide 사용하기
에어비엔비
Naver Map API 를 이용해서 지도를 띄우고 활용할 수 있음.
Mock API 에서 예약가능 숙소 목록을 받아와서 지도에 표시할 수 있음.
BottomSheetView 를 활용해서 예약 가능 숙소 목록을 인터렉션하게 표시할 수 있음.
ViewPager2 를 활용해서 현재 보고있는 숙소를 표시할 수 있음.
숙소버튼을 눌러 현재 보고 있는 숙소를 앱 외부로 공유할 수 있음.
1. Ratofit2 설정
레이아웃 설정 하기 전 우리가 사용하는 레이아웃 라이브러리는 retofit2, navermaps와 ViewPager2와 RecyclerView가 있다.
App 수준의 build.gradle에
implementation 'com.naver.maps:map-sdk:3.12.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'
implementation 'com.google.android.gms:play-services-location:18.0.0'
implementation을 추가해준다, retrofit을 추가 해준 이유는 mocky.id를 통해 json 형태의 http를 생성해주었기 때문에 그 http를 가져오기 위해 implementation을 해준 것이다. 아래의 주소가 mocky로 만든 더미 json http 입니다.
http://run.mocky.io/v3/be684b50-6883-4ca3-816d-64c7980c0738
Retrofit2이란?
- API 통신을 위해 구현된 OkHTTP의 HTTP 통신을 간편하게 만들어주는 라이브러리를 뜻합니다!
- Async Task가 없이 Background 쓰레드를 실행 → Call Back을 통해 Main Thread에서 UI 업데이트를 해줍니다.
🤔Retrofit2의 장점은?
가장 큰 장점 세개로 속도, 편의성, 가독성이 존재합니다.
- OkHTTP에서는 사용 시에 AsyncTask를 통해 비동기로 실행하여 속도가 느리다는 이슈가 있었습니다. 하지만 Retrofit2에서는 자체적 비동기 실행과 스레드 관리로 속도를 빠르게 올렸습니다.
- 함수 호출 시에 파라미터를 넘겨주면 되기에 작업량이 훨씬 줄어들었습니다.
- Interface 내에서 어노테이션을 사용하여 호출 함수를 미리 지정을 해두고, 구현 없이 해당 함수를 호출만 해주면 되기 때문에 코드를 읽기에 매우 편합니다. 또한 콜백 함수를 통한 직관적 설계가 가능합니다.
인터넷 권한 추가해주기
<uses-permission android:name="android.permission.INTERNET" />
- baseUrl 함수에는 절대로 변하지 않는 주소값을 넣어줍니다
- API의 주소가 https://api.github.com/users/HYE0N1127 일 시에 https://api.github.com/ 주소만 넣어주면된다.
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> {
override fun onResponse(call: Call<HouseDto>, response: Response<HouseDto>) {
if (response.isSuccessful.not()) {
//실패
Log.e("tag", "실패 !! 대실패 !!")
return
}
response.body()?.let { dto ->
updateMarker(dto.items)
viewPagerAdapter.submitList(dto.items)
recyclerViewAdapter.submitList(dto.items)
bottomSheetTitleTextView.text = "${dto.items.size}개의 숙소"
}
}
override fun onFailure(call: Call<HouseDto>, t: Throwable) {
}
}
)
}
}
- response 파라미터를 HouseDTO Data Class를 받아오는 것으로 설정을 해주었기에 받아온 데이터를 넣어줍니다.
- 로그를 이용하여 데이터가 잘 받아와졌는지 확인해주었습니다. 그리고 성공적으로 가져왔다면 response.body()를 통해 가져온 data를 Mapping 하여 활용 해줄 수 있다.
retofit을 불러오기 위해선 Retrofit.Builder()를 이용한다. 그리고 HouseService라는 @Get Interface를 따른 파일로 만들어준다.
package com.example.aop_part3_chaptor07
import retrofit2.Call
import retrofit2.http.GET
interface HouseService {
@GET("/v3/be684b50-6883-4ca3-816d-64c7980c0738")
fun getHouseList(): Call<HouseDto>
}
2. NaverMaps 사용
https://navermaps.github.io/android-map-sdk/guide-ko/
Home · 네이버 지도 안드로이드 SDK
No results matching ""
navermaps.github.io
navermap Api는 opensource Api로 네이버 지도를 내가 만든 앱에서 사용을 할 수 있게 해준다. 가장 중요한것은
https://console.ncloud.com/naver-service/application 여기서 api를 등록해주어야 한다. 로그인 후 카드까지 등록이 돼야 naver maps를 이용 할 수 있었다. navermaps api를 검색하면 이용 방법이 많이 나온다. !!
이렇게 사용 권한을 획득하고 implementations을 추가해주면
private lateinit var naverMap: NaverMap
네이버 맵을 불러올 수 있다.
그리고 overlide를 통해 navermap을 onMapReady()를 시켜준다
override fun onMapReady(navermap: NaverMap) {
naverMap = navermap
navermap.maxZoom = 18.0
navermap.minZoom = 10.0
val cameraUpdate = CameraUpdate.scrollTo(LatLng(37.447981, 126.884462)) // 위도 경도
navermap.moveCamera(cameraUpdate)
val uiSetting = naverMap.uiSettings
uiSetting.isLocationButtonEnabled = false
currentLocationButton.map = naverMap
locationSource = FusedLocationSource(this@MainActivity, LOCATION_PERMISSION_REQUEST)
naverMap.locationSource = locationSource
getHouseListFromApi()
}
- maxZoom은 18.0으로 가장 많이 축소시켰을 경우, 그리고 minZoomdms 10.0으로 가장 많이 확대시켰을 경우 설정해준 것이다.
- ScrollTo를 이용해 cameraupdate를 초기에 LatLng안에 내가 원하는 위도 경도로 설정 해줄수 있다.FusedLocationSource
- 네이버 지도 SDK는 Google Play 서비스의 FusedLocationProviderClient와 지자기, 가속도 센서를 활용해 최적의 위치를 반환하는 구현체인 FusedLocationSource를 제공한다
3. ViewPager2 사용
viewPager2는 기존에도 자주 사용해 보았던 Layout 라이브러리이다. 위 화면에 보이는 호텔들의 페이지를 넘길때 사용 되는 라이브러리로 사용 방법도 간단하다. 어뎁터를 추가하여 리스트에 있는 data을 Adpater를 통해 Layout으로 전달해주면 된다. 먼저 Layout에 viewpager를 사용코드를 작성해준다.
<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" />
그리고 메인 엑티비티에 불러와준다.
private val viewPager: ViewPager2 by lazy {
findViewById(R.id.houseViewPager)
}
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/palin"
}
startActivity(Intent.createChooser(intent, null))
})
여기서 viewpager어뎁터를 intent로 넘겨준 이유는 공유기능인 createChooser를 이용하였기 때문이다.
ㅁ Viewpager 어뎁터 사용하기
package com.example.aop_part3_chaptor07
import android.content.Context
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
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)
.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])
}
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
}
}
}
}
ㅁ Oncreate에서 불러와준다.
override fun onCreate(savedInstanceState: Bundle?) {
viewPager.adapter = viewPagerAdapter
viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
val selelctedHouseModel = viewPagerAdapter.currentList[position]
val cameraUpdate =
CameraUpdate.scrollTo(LatLng(selelctedHouseModel.lat, selelctedHouseModel.lng))
.animate(CameraAnimation.Easing) // 카메라가 이동할 때 애니메이션 주기 !
naverMap.moveCamera(cameraUpdate)
}
})
ㅁ registerOnPageChangeCallback을 해준이유는 viewpager를 넘길때마다 카메라도 같이 이동해줄 수 있도록 해주는 설정을 하기 위해 만들어 준 기능이다.
ㅁ RecyclerView 사용하기
recyclerview는 항상 사용해왔던 것 처럼 adapter 와 List data를 연동해주는 작업을 해준다. 여기서는 바텀 sheet를 통해 올리고 내릴 수 있는 include 를 통한 bottomSheet를 만들어준다,
여기서 app:layout_behavior 속성에 bottom_sheet을 넣고,
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
behavior_hideable 속성을 참으로 하고
behavior_peekHeight 속성의 높이를 100dp로 하였다.
hideable은 말 그대로 스왑해서 아래로 내렸을 때, 숨길 수 있냐를 설정하는 속성이고,
peekHeight는 minimize 되었을 때 최소 높이를 설정하는 속성이다.
두개를 같이 써보니, peekHeight가 설정되어 있어도 숨김이 된다. 창이 만들어졌을 땐 peekHeight로 표시된다.
<?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/shape_drawable"
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:gravity="center"
android:text="여러개의 숙소"
android:textColor="@color/black"
android:textSize="15sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
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_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/bottomSheetTitleTextView" />
</androidx.constraintlayout.widget.ConstraintLayout>
이렇게 바텀시트를 설정하고 사용하면 간단하게 이용 가능하다. 그리고 아까 viewpager2 어뎁터를 만든것 처럼 recyclerview 또한 어뎁터를 설정해준다.
ㅁ Recyclerview Adapter
package com.example.aop_part3_chaptor07
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
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_for_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
}
}
}
}
이렇게 model item듣과 match돼서 원하는 이미지와 제목, 내용등을 가져와서 쓸 수 있다.
ㅁ 전체 코드
<MainActiviy>
package com.example.aop_part3_chaptor07
import android.content.Intent
import android.graphics.Color
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.PersistableBundle
import android.util.Log
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.naver.maps.geometry.LatLng
import com.naver.maps.map.*
import com.naver.maps.map.overlay.Marker
import com.naver.maps.map.overlay.Overlay
import com.naver.maps.map.util.FusedLocationSource
import com.naver.maps.map.util.MarkerIcons
import com.naver.maps.map.widget.LocationButtonView
import retrofit2.*
import retrofit2.converter.gson.GsonConverterFactory
class MainActivity : AppCompatActivity(), OnMapReadyCallback, Overlay.OnClickListener {
private val viewPager: ViewPager2 by lazy {
findViewById(R.id.houseViewPager)
}
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/palin"
}
startActivity(Intent.createChooser(intent, null))
})
private val currentLocationButton: LocationButtonView by lazy {
findViewById(R.id.currentLocationButtonView)
}
private val recyclerViewAdapter = HouseListAdapter()
private val recyclerView: RecyclerView by lazy {
findViewById(R.id.recyclerView)
}
private lateinit var naverMap: NaverMap
private lateinit var locationSource: FusedLocationSource
private val mapView: MapView by lazy {
findViewById(R.id.mapView)
}
private val bottomSheetTitleTextView : TextView by lazy {
findViewById(R.id.bottomSheetTitleTextView)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mapView.onCreate(savedInstanceState)
mapView.getMapAsync(this)
recyclerView.adapter = recyclerViewAdapter
recyclerView.layoutManager = LinearLayoutManager(this)
viewPager.adapter = viewPagerAdapter
viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
val selelctedHouseModel = viewPagerAdapter.currentList[position]
val cameraUpdate =
CameraUpdate.scrollTo(LatLng(selelctedHouseModel.lat, selelctedHouseModel.lng))
.animate(CameraAnimation.Easing) // 카메라가 이동할 때 애니메이션 주기 !
naverMap.moveCamera(cameraUpdate)
}
})
}
override fun onMapReady(navermap: NaverMap) {
naverMap = navermap
navermap.maxZoom = 18.0
navermap.minZoom = 10.0
val cameraUpdate = CameraUpdate.scrollTo(LatLng(37.447981, 126.884462)) // 위도 경도
navermap.moveCamera(cameraUpdate)
val uiSetting = naverMap.uiSettings
uiSetting.isLocationButtonEnabled = false
currentLocationButton.map = naverMap
locationSource = FusedLocationSource(this@MainActivity, LOCATION_PERMISSION_REQUEST)
naverMap.locationSource = locationSource
getHouseListFromApi()
}
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
}
}
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> {
override fun onResponse(call: Call<HouseDto>, response: Response<HouseDto>) {
if (response.isSuccessful.not()) {
//실패
Log.e("tag", "실패 !! 대실패 !!")
return
}
response.body()?.let { qwe ->
updateMarker(qwe.items)
viewPagerAdapter.submitList(qwe.items)
recyclerViewAdapter.submitList(qwe.items)
Log.d("This","dto: ${qwe.items}")
bottomSheetTitleTextView.text = "${qwe.items.size}개의 숙소"
}
}
override fun onFailure(call: Call<HouseDto>, t: Throwable) {
}
}
)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray,
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode != LOCATION_PERMISSION_REQUEST) {
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, outPersistentState: PersistableBundle) {
super.onSaveInstanceState(outState, outPersistentState)
mapView.onSaveInstanceState(outState)
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
// 메모리가 별로 없을 때 호출 되는 함수
override fun onLowMemory() {
super.onLowMemory()
mapView.onLowMemory()
}
companion object {
private const val LOCATION_PERMISSION_REQUEST = 1000
}
override fun onClick(overlay: Overlay): Boolean {
val selectedModel = viewPagerAdapter.currentList.firstOrNull {
it.id == overlay.tag
}
selectedModel?.let {
val position = viewPagerAdapter.currentList.indexOf(it)
viewPager.currentItem = position
}
return true
}
}
<Activity main.xml>
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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=".MainActivity">
<com.naver.maps.map.MapView
android:id="@+id/mapView"
android:layout_marginBottom="80dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<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/currentLocationButtonView"
android:layout_gravity="top|start"
android:layout_margin="12dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<include layout="@layout/bottom_sheet" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<HouseModel>
package com.example.aop_part3_chaptor07
data class HouseModel (
val id: Int,
val title : String,
val price: String,
val imgUrl:String,
val lat: Double,
val lng: Double
)
<HouseModelDto>
package com.example.aop_part3_chaptor07
data class HouseDto (
val items: List<HouseModel>
)
<HouseListAdapter>
package com.example.aop_part3_chaptor07
import android.content.Context
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
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)
.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])
}
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
}
}
}
}
'안드로이드 > 앱개발(Android)' 카테고리의 다른 글
(Android) 음악스트리밍 앱 (0) | 2022.02.20 |
---|---|
(Android) 유튜브 앱 (0) | 2022.02.12 |
(Android) 중고거래 앱 (0) | 2022.02.12 |
(Android) 틴더앱 (0) | 2022.02.12 |
(코틀린 kotlin) 도서 리뷰 앱 (0) | 2022.02.12 |