[Android Kotlin 기초] 7-5. DiffUtil and data binding with RecyclerView

DiffUtil and data binding with RecyclerView

1.RecyclerView에서 notifiyDataSetChanged 메소드는 매우 효율적이지 못하다.

var data =  listOf<SleepNight>()
   set(value) {
       field = value
       notifyDataSetChanged()
   }

위 에서 사용할 시 1개라도 아이템이 수정,삽입,삭제가 됬을 시에 모든 아이템 데이터를 UI갱신 처리하기 때문에 앱 사용자 기준으로 앱 화면이 깜빡이는 것처럼 느껴진다.

RecyclerView는 DiffUtil를 제공한다. DiffUtil 를 통해 일부 아이템만 업데이트 할 수가 있다.

2. DiffUtill 사용 .

class SleepNightDiffCallback : DiffUtil.ItemCallback<SleepNight>() {
    override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
                                                                                
    }

    override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {

    }

}

클래스에 DiffUtil.ItemCallBack<사용하는 엔티티="">를 상속하고 두 개의 함수를 오버라이드 합니다.

areItemsTheSame 함수

 override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
        return oldItem.nightId == newItem.nightId
    }

areItemsTheSame 함수

override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
   return oldItem == newItem
}

3. ListAdapter 활용

class SleepNightAdapter: ListAdapter<SleepNight, SleepNightAdapter.ViewHolder>(SleepNightDiffCallback()) 

그 다음 ListAdapter 를 어댑터로 사용할 클래스에 상속을 하고 인자 값으로 DiffUtil.ItemCallback를 상속받아 정의를 했던 클래스를 사용해줍니다.

그리고 사용자가 [Adapter] 내에서 DataList를 정의하지 않고 [List] 자체에서 DataList를 정의하기 때문에 제너릭 타입 인자값으로 해당 DataClass를 넣어줍니다.

-ListAdapter에서 사용할 수 있는 주요 Method-

getItem(position: Int) : protected 매서드라 클래스 내부에서 구현할 때 사용합니다. 어댑터 내 List Indexing을 할 때 활용됩니다 getCurrentList() : 어댑터가 가지고 있는 리스트를 가져올 때 사용합니다 submitList(MutableList list) : 리스트 항목을 변경하고 싶을 때 사용합니다

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = getItem(position)
        holder.bind(item)
    }

기존 list[adapterPosition]을 대신해서 getItem(position) 함수를 통해서 값을 전달해줍니다.

4. submitList() 를 사용해서 리스트 업데이트

sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
   it?.let {
       adapter.submitList(it)
   }
})

submitList() 를 통해서 어떤 타이밍에 어댑터를 업데이트 할 것인지 지정합니다. 라이브데이터와 같이 쓰면 상당히 효율적인 DiffUtil 같습니다.

5. RecyclerView에서 데이터 바인딩 사용하기

xml 에서 레이아웃 추가 후 RecyclerView Adapter의 onCreateViewHolder함수 에서

val view = layoutInflater
       .inflate(R.layout.list_item_sleep_night, parent, false)

return ViewHolder(view)

위처럼 사용했던 것을 삭제 후

val binding =
ListItemSleepNightBinding.inflate(layoutInflater, parent, false)

return ViewHolder(binding)

사용합니다.

뷰홀더 클래스는

class ViewHolder private constructor(val binding: ListItemSleepNightBinding) : RecyclerView.ViewHolder(binding.root){

바인딩 객체를 받게끔 하고 binding 변수로 findViewById 없이 사용이 가능합니다.

6. 바인딩 어댑터 사용

@BindingAdapter("sleepQualityString")
fun TextView.setSleepQualityString(item: SleepNight) {
   text = convertNumericQualityToString(item.sleepQuality, context.resources)
}

위처럼 설정한 뒤 xml 에서 해당 뷰에를 사용해서 텍스트를 바꿀 수 있다.

 <data>

        <variable
            name="sleep"
            type="com.example.android.trackmysleepquality.database.SleepNight" />
    </data>
...
<TextView>
...
 app:sleepQualityString="@{sleep}" 
...
binding.sleep = item