배경
기존에는 하나의 RecyclerView에 같은 타입의 뷰만 띄웠지만 여러 타입의 뷰를 띄워야 하는 경우도 있습니다.
이런 경우 RecyclewView.Adapter를 상속하는 CommonAdapter를 구현하여 Multi-View Type이 적용되도록 하는 방법을 알아보겠습니다
Data는 아래와 같이 viewType과 type에 따른 viewObject가 제공됩니다
{
"viewItems": [
{
"viewType": "TWO_LINE_TEXT",
"viewObject": {
"titleText": "서울대입구역",
"descText": "서울특별시 관악구 남부순환로 지하 1822"
}
},
{
"viewType": "ONE_LINE_TEXT",
"viewObject": {
"titleText": "서울대입구역"
}
},
{
"viewType": "ONE_IMAGE",
"viewObject": {
"imageVO": {
"url": "https://dimg.donga.com/wps/NEWS/IMAGE/2022/01/28/111500268.2.jpg",
"width": 1024,
"height": 765
}
}
}
]
}
구현
CommonItem.kt
/* 모든 유형의 Item은 viewType, viewObject 형태로 제공됩니다 */
data class CommonItem(
val viewType: String,
val viewObject: ViewObject
)
ViewObject.kt
/* viewType 종류에 따라 Object를 생성해줘야 합니다 */
sealed class ViewObject {
data class OneImageViewObject(
val imageVO: ImageVO
) : ViewObject()
data class OneLineTextViewObject(
val contents: String
) : ViewObject()
data class TwoLineTextViewObject(
val title: String,
val contents: String
) : ViewObject()
}
data class ImageVO(
val url: String,
val width: Int,
val height: Int
)
ViewType.kt
/* 새로운 뷰타입이 생길 때마다 업데이트 해야 합니다 */
enum class CommonViewType(viewType: String) {
ONE_LINE_TEXT("ONE_LINE_TEXT"),
TWO_LINE_TEXT("TWO_LINE_TEXT"),
ONE_IMAGE("ONE_IMAGE")
}
RecyclerView
CommonViewHolder.kt
/* 새로운 뷰타입이 생길 때마다 ViewHolder를 추가해야 합니다 */
sealed class CommonViewHolder(
binding: ViewDataBinding
) : RecyclerView.ViewHolder(binding.root) {
abstract fun bind(item: CommonItem)
class OneLineTextViewHolder(
private val binding: ItemOneLineTextBinding
) : CommonViewHolder(binding) {
override fun bind(item: CommonItem) {
val viewObject = item.viewObject as ViewObject.OneLineTextViewObject
binding.content.text = viewObject.contents
}
}
class TwoLineTextViewHolder(
private val binding: ItemTwoLineTextBinding
) : CommonViewHolder(binding) {
override fun bind(item: CommonItem) {
val viewObject = item.viewObject as ViewObject.TwoLineTextViewObject
binding.title.text = viewObject.title
binding.content.text = viewObject.contents
}
}
class OneImageViewHolder(
private val binding: ItemOneImageBinding
) : CommonViewHolder(binding) {
override fun bind(item: CommonItem) {
val viewObject = item.viewObject as ViewObject.OneImageViewObject
Glide.with(binding.root)
.load(viewObject.imageVO.url)
.into(binding.image)
}
}
}
기존의 Adapter 내부에서 Recycler.ViewHolder를 만드는 과정을 CommonViewHolder로 추출하여 따로 처리하는 과정을 거칩니다. bind의 경우 데이터가 들어왔을 때 뷰에 적용하는 과정을 각각의 ViewHolder에 맞게끔 작업하기 위해 abstract로 선언하였습니다
CommonAdapter.kt
class CommonListAdapter(
private val dataSet: ArrayList<CommonItem>
) : RecyclerView.Adapter<CommonViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommonViewHolder {
/* 새로운 뷰타입이 생길 때마다 분기를 추가 */
return when(viewType) {
CommonViewType.ONE_LINE_TEXT.ordinal -> {
CommonViewHolder.OneLineTextViewHolder(
getViewDataBinding(parent, R.layout.item_one_line_text))
}
CommonViewType.TWO_LINE_TEXT.ordinal -> {
CommonViewHolder.TwoLineTextViewHolder(
getViewDataBinding(parent, R.layout.item_two_line_text))
}
else -> {
CommonViewHolder.OneImageViewHolder(
getViewDataBinding(parent, R.layout.item_one_image))
}
}
}
override fun onBindViewHolder(holder: CommonViewHolder, position: Int) {
holder.bind(dataSet[position])
}
override fun getItemCount(): Int = dataSet.size
/* CommonViewType에서 해당 data의 viewType의 ordinal(인덱스)를 반환 */
override fun getItemViewType(position: Int): Int {
return CommonViewType.valueOf(dataSet[position].viewType).ordinal
}
/* binding 생성 */
private fun <T: ViewDataBinding> getViewDataBinding(parent: ViewGroup, layoutRes: Int) : T {
return DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
layoutRes,
parent,
false
)
}
}
getItemViewType 함수를 통해 현재 아이템의 viewType에 해당하는 CommonViewType의 ordinal(인덱스)를 반환하며
반환된 값은 onCreateViewHolder로 연결됩니다.
그 이후 Factory Method 패턴을 적용하고 다른 코드들을 정리하여 최종적으로 깃허브에 올려놨습니다! 한 번 참고해주세요!!
<참고>
반응형
'Android' 카테고리의 다른 글
[SWM Mobile] CommonListAdapter (6/30) (0) | 2022.07.06 |
---|---|
[SWM Mobile] CommonListAdapter - JSON (6/23) (0) | 2022.07.04 |
[Android] CLEARTEXT communication to 'IP' not permitted by network security policy (0) | 2022.06.27 |
[Android] ViewPager2와 ViewPager의 차이점 (0) | 2022.06.26 |
[Android] ViewPager 사용법 (0) | 2022.06.26 |
댓글