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 함수
- 이전 어댑터와 바뀌는 어댑터의 아이템이 동일한지 체크 할 수 있습니다. 주로 db의 idx를 기준으로 서로를 비교합니다.
override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
return oldItem.nightId == newItem.nightId
}
areItemsTheSame 함수
- 이전 어댑터와 바뀌는 어댑터의 아이템 내부 내용이 동일 한지 비교를 합니다. 위에 areItemsTheSame 함수가 true 를 반환 했을 시 추가적으로 비교하기 위해 사용이 됩니다.
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를 넣어줍니다.
- 사용자가 직접 DataList를 정의하지 않기 때문에 어댑터에서 getItemCount() 함수는 쓰지 않습니다.
-ListAdapter에서 사용할 수 있는 주요 Method-
getItem(position: Int) : protected 매서드라 클래스 내부에서 구현할 때 사용합니다.
어댑터 내 List Indexing을 할 때 활용됩니다
getCurrentList() : 어댑터가 가지고 있는 리스트를 가져올 때 사용합니다
submitList(MutableList
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