하나의 리사이클러뷰에서 위의 카카오톡 대화창과 같이 여러 뷰타입을 사용할 수 있다.
2021.08.03 - [안드로이드/기본] - [Android] 리사이클러뷰 갱신 효율성을 위한 ListAdapter 사용하기
위의 코드를 기반으로 작성하였으며
리사이클러뷰 어댑터 작성 과정은 생략하였다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_launcher_foreground" />
<TextView
android:id="@+id/name_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="16dp"
android:text="TextView"
android:textSize="30sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/description_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="@+id/name_text"
app:layout_constraintTop_toBottomOf="@+id/name_text" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_launcher_foreground" />
<TextView
android:id="@+id/name_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="TextView"
android:textSize="30sp"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/imageView"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/description_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:textSize="16sp"
app:layout_constraintEnd_toStartOf="@+id/imageView"
app:layout_constraintTop_toBottomOf="@+id/name_text" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
먼저 사용할 뷰타입 레이아웃들을 만들어준다.
왼쪽 정렬과 오른쪽 정렬된 두가지의 레이아웃을 만들어 주었다.
class Constants {
companion object{
const val LEFT_POSITION = 0
const val RIGHT_POSITION = 1
}
}
타입을 구분하기 위한 상수를 정의해주고
@Entity(tableName = "item_table")
data class Item(
// autoGenerate = true , 자동으로 PrimaryKey 생성해줌
@PrimaryKey(autoGenerate = true)
val id : Int,
val name : String,
val description : String,
val type : Int
)
사용할 데이터에서 타입을 구분하기 위한
type 파라미터를 만들어준다.
class MyAdapter : ListAdapter<Item, RecyclerView.ViewHolder>(diffUtil) {
// 왼쪽 뷰홀더
inner class LeftViewHolder(private val binding : LayoutItemBinding) : RecyclerView.ViewHolder(binding.root){
fun bind(item : Item){
binding.apply {
nameText.text = item.name
descriptionText.text = item.description
}
}
}
// 오른쪽 뷰홀더
inner class RightViewHolder(private val binding : LayoutItemRightBinding) : RecyclerView.ViewHolder(binding.root){
fun bind(item : Item){
binding.apply {
nameText.text = item.name
descriptionText.text = item.description
}
}
}
// 타입에 따라 다른 뷰홀더 생성
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
// viewType = getItemViewType 의 리턴 값
return when(viewType){
LEFT_POSITION ->{
val binding = LayoutItemBinding.inflate(LayoutInflater.from(parent.context),parent,false)
LeftViewHolder(binding)
}
RIGHT_POSITION ->{
val binding = LayoutItemRightBinding.inflate(LayoutInflater.from(parent.context),parent,false)
RightViewHolder(binding)
}
else -> throw RuntimeException("Error")
}
}
// 타입에 따라 바인딩
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val currentItem = getItem(position)
when(currentItem.type){
LEFT_POSITION -> (holder as LeftViewHolder).bind(currentItem)
RIGHT_POSITION -> (holder as RightViewHolder).bind(currentItem)
}
}
// 리스트 갱신
override fun submitList(list: List<Item>?) {
super.submitList(list)
}
// 아이템 타입 리턴
override fun getItemViewType(position: Int): Int {
return getItem(position).type
}
// diffUtil 추가
companion object{
val diffUtil = object : DiffUtil.ItemCallback<Item>(){
override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
return oldItem.hashCode() == newItem.hashCode()
}
}
}
}
전체 코드이다.
하나씩 설명하자면
getItemViewType을 오버라이드하여 현재아이템의 뷰타입 리턴값을
아이템의 type 값으로 설정해준다.
이제 리턴받은 viewType에 따라 다른 뷰홀더를 생성해준다.
뷰바인딩이 사용되었다.
이제 타입에 맞는 바인딩을 호출해준다.
타입에 따라 각각 데이터를 바인딩해준다.
예시로 임의의 값을 왼쪽이나 오른쪽에 추가할 수 있도록 하였다.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var adapter: MyAdapter
private val viewModel by viewModels<ItemViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 뷰 바인딩
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// 어댑터 연결
adapter = MyAdapter()
// 아이템 변경시 갱신
viewModel.readAllData.observe(this){
adapter.submitList(it)
}
binding.apply {
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this@MainActivity)
// 왼쪽에 추가
leftButton.setOnClickListener {
val item = Item(0,nameText.text.toString(),descriptionText.text.toString(),LEFT_POSITION)
viewModel.addItem(item)
}
// 오른쪽에 추가
rightButton.setOnClickListener {
val item = Item(0,nameText.text.toString(),descriptionText.text.toString(), RIGHT_POSITION)
viewModel.addItem(item)
}
deleteButton.setOnClickListener{
viewModel.deleteAll()
}
}
}
}
왼쪽, 오른쪽 버튼 클릭 시 각각 type에 맞는 값을 추가하였다.
잘 구현된 것을 확인할 수 있다.
https://github.com/HanYeop/AndroidStudio-Practice2/tree/master/RecyclerViewEx2
참고
https://huiveloper.tistory.com/21