안녕하세요.
이번 포스팅에서는 리사이클러뷰를 diffUtil과 함께 사용해 보겠습니다.
언어: 코틀린
sdk vsersion
- compile: 33
- min: 21
- target: 33
먼저 프래그먼트를 하나 생성한 후, xml을 아래와 같이 수정합니다.
저는 RecyclerFragment라는 이름으로 생성하였습니다.
fragment_recycler.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".RecyclerFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/sample_recycler_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="10dp"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
프래그먼트에서는 view binding을 사용하도록 수정합니다.
뷰바인딩에 관한 내용은 아래 링크를 참고해 주세요.
https://it-of-fortune.tistory.com/21
RecyclerFragment.kt
class RecyclerFragment : Fragment() {
private var _binding: FragmentRecyclerBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentRecyclerBinding.inflate(layoutInflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
RecyclerView에서 보여줄 개별 item layout이 필요합니다.
item view xml을 생성합니다(figure1, figure2 참조).
그리고 아래와 같이 수정해 줍니다.
view_sample_item.xml
<?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="140dp"
android:layout_height="40dp"
android:layout_marginVertical="5dp"
android:background="@color/black">
<ImageView
android:id="@+id/sample_image"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="10dp"
android:src="@drawable/ic_launcher_background"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/sample_data"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:gravity="center"
android:textColor="@color/white"
app:layout_constraintStart_toEndOf="@id/sample_image"
tools:text="1" />
</androidx.constraintlayout.widget.ConstraintLayout>
위의 작업이 끝난 후 프래그먼트의 xml에서 item의 preview를 확인하는 코드를 추가할 수 있습니다.
fragment_recycler.xml
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/sample_recycler_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="10dp"
app:layout_constraintStart_toStartOf="parent"
tools:listitem="@layout/view_sample_item"/>
tools:listitem="@layout/view_sample_item"
위의 코드를 추가하면 item preview가 확인됩니다(figure3 참조).
이제 샘플 아이템을 생성하겠습니다.
Data class를 생성합니다(figure4, figure5 참조).
아래와 같이 작성합니다.
SampleData.kt
data class SampleData(
val data: Int
)
위에서 만든 data class를 이용해, 프래그먼트에서 실질적인 데이터를 생성해 주겠습니다.
RecyclerFragment.kt
class RecyclerFragment : Fragment() {
private var _binding: FragmentRecyclerBinding? = null
private val binding get() = _binding!!
private val sampleItems = ArrayList<SampleData>()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentRecyclerBinding.inflate(layoutInflater, container, false)
setSampleItems()
return binding.root
}
private fun setSampleItems() {
for (i in 1..30) {
sampleItems.add(
SampleData(
i
)
)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
1~30까지의 데이터를 생성해 주도록 작성했습니다.
다음으로, recyclerView에 item을 장착시켜 줄 adpater를 생성해야 합니다.
새로운 kotlin class를 생성합니다. 저는 SampleAdapter.kt로 생성했습니다.
그리고 아래 코드를 추가합니다.
SampleAdapter.kt
class SampleAdapter : ListAdapter<SampleData, SampleAdapter.SampleDataViewHolder?>() {
class SampleDataViewHolder(
private val binding: ViewSampleItemBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind() {
}
}
}
위의 코드를 작성할 때 ListAdapter의 import가 아래와 같은지 확인해 주세요.
import androidx.recyclerview.widget.ListAdapter
작성을 마친 뒤, class 이름을 클릭하고 Alt + Enter를 입력하여 Implement members를 선택해 줍니다(figure6, figure7 참조).
위의 과정을 마치면 아래와 같은 코드를 갖게 됩니다.
SampleAdapter.kt
class SampleAdapter : ListAdapter<SampleData, SampleAdapter.SampleDataViewHolder?>() {
class SampleDataViewHolder(
private val binding: ViewSampleItemBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind() {
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SampleDataViewHolder {
TODO("Not yet implemented")
}
override fun onBindViewHolder(holder: SampleDataViewHolder, position: Int) {
TODO("Not yet implemented")
}
}
여기서 몇 가지 추가하여 다음과 같이 작성합니다.
SampleAdapter.kt
class SampleAdapter : ListAdapter<SampleData, SampleAdapter.SampleDataViewHolder?>(
SampleDataCallback
) {
class SampleDataViewHolder(
private val binding: ViewSampleItemBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: SampleData) {
binding.sampleData.text = item.data.toString()
}
}
companion object {
private val SampleDataCallback = object : DiffUtil.ItemCallback<SampleData>() {
override fun areItemsTheSame(
oldItem: SampleData,
newItem: SampleData
): Boolean {
return oldItem.hashCode() == newItem.hashCode()
}
override fun areContentsTheSame(
oldItem: SampleData,
newItem: SampleData
): Boolean {
return oldItem == newItem
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SampleDataViewHolder {
val binding =
ViewSampleItemBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return SampleDataViewHolder(binding)
}
override fun onBindViewHolder(holder: SampleDataViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
간략하게 설명하자면, data를 textView에 보여주는 코드와 data의 갱신이 일어났을 경우 recyclerView에 새로운 item들을 넣어주는 diffUtil의 코드를 추가했습니다.
마지막으로, 프래그먼트에서 이 어답터를 사용하도록 합니다.
RecyclerFragment.kt
private val sampleAdapter by lazy {
SampleAdapter()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
...
binding.sampleRecyclerView.adapter = sampleAdapter
binding.sampleRecyclerView.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
sampleAdapter.submitList(sampleItems)
...
}
Linear 그리고 vertical의 형태로 item이 배치되도록 추가했습니다.
sampleAdapter.submitList(sampleItems)
이 코드로 인해 recycler view에 들어갈 아이템 리스트를 adapter에 전해주게 됩니다.
에뮬레이터에서 코드를 실행해 보겠습니다.
문제없이 동작합니다.
하지만 지금 이 상태에서는 diffUtil은 큰 의미가 없습니다. data의 변화가 없기 때문입니다.
그렇다면 diffUtil을 위해 약간의 수정을 해보겠습니다.
fragment_recycler.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".RecyclerFragment">
<Button
android:id="@+id/item_add_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="+"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/sample_recycler_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="10dp"
app:layout_constraintStart_toStartOf="parent"
tools:listitem="@layout/view_sample_item"/>
</androidx.constraintlayout.widget.ConstraintLayout>
버튼을 하나 추가합니다.
프래그먼트를 다음과 같이 수정합니다.
RecyclerFragment.kt
class RecyclerFragment : Fragment() {
private var _binding: FragmentRecyclerBinding? = null
private val binding get() = _binding!!
private var currentNumber: Int = 0
private val sampleItems = ArrayList<SampleData>()
private val sampleAdapter by lazy {
SampleAdapter()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentRecyclerBinding.inflate(layoutInflater, container, false)
binding.itemAddButton.setOnClickListener {
sampleItems.add(
SampleData(
currentNumber
)
)
sampleAdapter.submitList(sampleItems.toMutableList())
currentNumber += 1
}
binding.sampleRecyclerView.adapter = sampleAdapter
binding.sampleRecyclerView.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
+버튼을 클릭하면 currentNumber를 사용해 데이터가 생성되고 데이터 리스트에 넣어준 뒤 submitList를 진행합니다. 그 후 숫자 1이 더해집니다. Button을 계속 누르면 위의 과정이 반복됩니다.
이제는 submitList를 하면 diffUtil이 데이터의 변화를 판단하여 리스트를 갱신하게 됩니다.
의도한 동작은 버튼을 클릭할 때마다 새로운 데이터를 리스트에 추가합니다.
에뮬레이터에서 실행해 보겠습니다.
문제없이 동작합니다.
이상 포스팅을 마치겠습니다.
감사합니다.
'Android Application > 기초 사용법' 카테고리의 다른 글
안드로이드 ML Kit - QR scan 구현 (0) | 2023.04.01 |
---|---|
안드로이드 힐트(hilt) 의존성 주입 사용 (1) | 2023.03.31 |
안드로이드 네비게이션(navigation) 사용 (0) | 2023.03.30 |
안드로이드 데이터바인딩(dataBinding) 사용 (0) | 2023.03.29 |
안드로이드 뷰모델(ViewModel) 사용하기(with LiveData) (0) | 2023.03.28 |