RecyclerView를 만들때 구분해서 만들 수 있다. 예를 들면, 메신저의 채팅 목록이다.
기존에 포스팅 했던 리사이클러뷰와 크게 다르지 않다. 두 가지 타입으로 나누어서 구현을 했다. 타이틀이 적혀있는 header 부분과 내용이 있는 content 부분으로 나누었다.
이제 코드를 나누어서 봐보자.
sealed class MainViewData(open val type: Int) {
data class HeaderData(
val title: String,
override val type: Int
) : MainViewData(type)
data class ContentData(
val storeItems: StoreItem,
override val type: Int
) : MainViewData(type)
}
먼저, view type을 구분하기 쉽게 sealed class로 묶어주었다. 둘 다 필요한 데이터와 타입 정보를 가지고 있다.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
HEADER_TYPE -> {
HeaderViewHolder(
ItemHeaderBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
else -> {
ContentViewHolder(
ItemContentBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
}
}
뷰홀더를 생성할 때 타입에 따라 구분해서 생성해줘야 한다. 인자로 넘어오는 viewType으로 구분해서 뷰홀더를 생성해준다. 이때 getItemViewType() 함수를 오버라이딩 해줘야 하는데, 여기서 반환하는 값이 onCreateViewHolder의 인자로 넘어간다. 따라서 타입별로 구분할 수 있다.
override fun getItemViewType(position: Int): Int {
return dataList.value?.get(position)?.type ?: 0
}
참고로, 아래 함수의 반환값이 0이 되면 화면에 아무것도 보이지 않게 된다.
override fun getItemCount() = dataList.value?.size ?: 0
onCreateViewHolder에서 반환했던 값이 이 함수의 viewHolder 인자로 넘어오는 것 같다. 이때도, 타입별로 뷰 홀더를 캐스팅 해주고 바인딩 해준다.
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
when (val mainViewData = dataList.value?.get(position) ?: 0) {
is MainViewData.HeaderData -> {
(viewHolder as HeaderViewHolder).bind(mainViewData)
}
is MainViewData.ContentData -> {
(viewHolder as ContentViewHolder).bind(mainViewData)
}
}
}
각각의 뷰 홀더 클래스를 정의해주고, 필요한 로직을 구현해주면 된다. HeaderViewHolder에서는 단순히 textView에 내용을 적어주기만 한다.
class HeaderViewHolder(binding: ItemHeaderBinding) : RecyclerView.ViewHolder(binding.root) {
private val title = binding.tvTitle
fun bind(item: MainViewData.HeaderData) {
title.text = item.title
}
}
class MainViewAdapter(private val dataList: LiveData<MutableList<MainViewData>>) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
HEADER_TYPE -> {
HeaderViewHolder(
ItemHeaderBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
else -> {
ContentViewHolder(
ItemContentBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
}
}
override fun getItemViewType(position: Int): Int {
return dataList.value?.get(position)?.type ?: 0
}
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
when (val mainViewData = dataList.value?.get(position) ?: 0) {
is MainViewData.HeaderData -> {
(viewHolder as HeaderViewHolder).bind(mainViewData)
}
is MainViewData.ContentData -> {
(viewHolder as ContentViewHolder).bind(mainViewData)
}
}
}
override fun getItemCount() = dataList.value?.size ?: 0
class ContentViewHolder(binding: ItemContentBinding) : RecyclerView.ViewHolder(binding.root) {
private val img = binding.vImg
private val title = binding.tvTitle
private val body = binding.tvBody
private val someId = binding.tvSomeId
private val someIdLine = binding.tvSomeIdLine
private val eventContainer = binding.rlEvent
private val lanchingContainer = binding.rlLanching
private val regexTitle = Regex("(?<=\\] )(.*?)(?= \\d)")
fun bind(item: MainViewData.ContentData) {
title.text = regexTitle.find(item.storeItems.title)?.value ?: ""
body.text = item.storeItems.description
someId.text = item.storeItems.s_price
if (item.storeItems.n_price != null) {
someIdLine.text = "${item.storeItems.n_price}원"
someIdLine.paintFlags = someIdLine.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
}
if (item.storeItems.badge.isNullOrEmpty()) {
eventContainer.visibility = View.GONE
lanchingContainer.visibility = View.GONE
} else if (item.storeItems.badge.size == 2) {
eventContainer.visibility = View.VISIBLE
lanchingContainer.visibility = View.VISIBLE
} else if (item.storeItems.badge[0] == "이벤트특가") {
eventContainer.visibility = View.VISIBLE
lanchingContainer.visibility = View.GONE
} else {
eventContainer.visibility = View.GONE
lanchingContainer.visibility = View.VISIBLE
}
}
}
class HeaderViewHolder(binding: ItemHeaderBinding) : RecyclerView.ViewHolder(binding.root) {
private val title = binding.tvTitle
fun bind(item: MainViewData.HeaderData) {
title.text = item.title
}
}
companion object {
const val HEADER_TYPE = 1
const val CONTENT_TYPE = 2
}
}
어댑터는 모두 구현이 되었고, 이제 메인 액티비티에 연결만 해주면 된다.
binding.rvMain.adapter = MainViewAdapter(viewDataList)