리사이클러뷰가 멀티뷰 타입을 지원한다는 것을 알게 되어 간단한 샘플을 만들어 내용을 이해해 보려 한다. 멀티뷰 타입을 적용하면 한 개의 리스트에서 여러 유형의 아이템을 보여줄 수 있으므로 여러 개의 리스트를 만들지 않아도 돼서 많이 유용할 것 같다 ! 🤗
29CM 앱의 MY 탭을 클론해봤다 !
나의 쇼핑정보
와주문배송조회
부분을 두 개의 뷰로 나눠서 만들었다. 🤓
sealed class
나 enum class
등을 사용하여 다른 뷰 유형을 정의한다.getItemViewType
함수를 오버라이드하여 각 아이템에 대한 뷰 유형을 결정한다.onCreateViewHolder
에서 뷰 유형에 따라 다른 뷰 홀더를 생성한다. onBindViewHolder
에서 생성된 해당 뷰에 대한 데이터를 바인딩 해준다.
sealed
클래스는 자기 자신이 추상 클래스이고, 자신을 상속 받는 여러 서브 클래스들을 가질 수 있다. 이를 사용하면enum
클래스와 달리 상속을 지원하기 때문에, 상속을 활용한 풍부한 동작을 구현할 수 있다.
또한, 자신을 상속 받는 서브 클래스의 종류를 제한 할 수 있다.
- sealed 클래스의 서브 클래스들은 반드시 같은 파일 내에 선언 되어야 한다.
- sealed 클래스의 서브 클래스를 상속한 클래스들은 같은 파일 내에 없어도 된다.
- sealed 클래스는 기본적으로 추상 클래스이다.
- sealed 클래스는 private 생성자만 갖게 된다.
sealed class Items {
data class TitleItem(val title: String) : Items()
data class ContentItem(val content: String) : Items()
}
onCreateViewHolder
가 호출 되기 전getItemViewType(position: Int): Int
함수가 먼저 호출되어 리턴 값이 넘겨진다. 그러므로 해당 함수에서ViewType
을 구분하여 리턴해준다.- 뷰타입에 해당하는
ViewHolder
를 여러 개 만들고onCreateViewHolder
에서 데이터에 따라 그에 해당하는ViewHolder
를 생성해준다.
class ItemAdapter(val items: MutableList<Items>) : RecyclerView.Adapter<ViewHolder>() {
companion object {
private const val VIEW_TYPE_TITLE = 0
private const val VIEW_TYPE_CONTENT = 1
}
interface ItemClick {
fun onItemClick(view: View, position: Int)
}
var itemClick: ItemClick? = null
/**
getItemViewType의 리턴값 Int가 viewType으로 넘어온다.
viewType으로 넘어오는 값에 따라 viewHolder를 알맞게 처리해주면 된다.
*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
VIEW_TYPE_TITLE -> {
val binding = ItemTitleBinding.inflate(inflater, parent, false)
TitleViewHolder(binding)
}
else -> {
val binding = ItemContentBinding.inflate(inflater, parent, false)
ContentViewHolder(binding)
}
}
}
// 데이터의 index
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
when (val item = items[position]) {
is Items.TitleItem -> {
(holder as TitleViewHolder).title.text = item.title
}
is Items.ContentItem -> {
(holder as ContentViewHolder).content.text = item.content
holder.itemView.setOnClickListener {
itemClick?.onItemClick(it, position)
}
}
}
}
override fun getItemViewType(position: Int): Int {
return when (items[position]) {
is Items.TitleItem -> VIEW_TYPE_TITLE
is Items.ContentItem -> VIEW_TYPE_CONTENT
}
}
inner class TitleViewHolder(binding: ItemTitleBinding) :
RecyclerView.ViewHolder(binding.root) {
val title = binding.tvTitle
}
inner class ContentViewHolder(binding: ItemContentBinding) :
RecyclerView.ViewHolder(binding.root) {
val content = binding.tvContent
}
}
class MainActivity : AppCompatActivity() {
private val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
initView()
}
private fun initView() {
val dataList = mutableListOf( // 샘플 리스트 생성
Items.TitleItem("나의 쇼핑정보"),
Items.ContentItem("주문배송조회"),
Items.ContentItem("숙박예약조회"),
Items.ContentItem("취소/교환/반품 내역"),
Items.ContentItem("상품 리뷰"),
Items.TitleItem("나의 계정정보"),
Items.ContentItem("회원 정보 수정"),
Items.ContentItem("나의 체형 정보"),
Items.ContentItem("회원등급"),
Items.ContentItem("쿠폰"),
Items.ContentItem("마일리지"),
Items.ContentItem("설정"),
Items.TitleItem("영상/뉴스"),
Items.ContentItem("1:1 문의내역"),
Items.ContentItem("상품 Q&A내역"),
Items.ContentItem("FAQ"),
Items.ContentItem("고객의 소리"),
Items.TitleItem("ABOUT 29CM"),
Items.ContentItem("공지사항"),
Items.ContentItem("인재채용"),
Items.ContentItem("29CM 소개"),
)
val adapter = ItemAdapter(dataList) // 어댑터 생성
binding.recyclerView.adapter = adapter // 어댑터 연결
binding.recyclerView.layoutManager = LinearLayoutManager(this)
adapter.itemClick = object : ItemAdapter.ItemClick {
override fun onItemClick(view: View, position: Int) {
// TODO 아이템 클릭했을 때 액션
}
}
}
}
<?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="wrap_content">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:padding="@dimen/list_item_padding"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:layout_width="match_parent"
android:layout_height="3dp"
android:background="@color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
</androidx.constraintlayout.widget.ConstraintLayout>
<?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="wrap_content">
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/list_item_padding2"
app:layout_constraintEnd_toEndOf="parent"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/view" />
<View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#A9AAAAAA"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<?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="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>