RecyclerView를 사용하다 데이터의 변화가 있을 때 리스트를 업데이트하는 5가지 방법이 있다.
notifyDataSetChanged - 리스트 전체 업데이트
notifyItemChanged, notifyItemRangeChanged - 특정 범위의 아이템만 변경
notifyItemInserted, notifyItemRangeInserted - 특정 위치에 아이템 새로 추가
notifyItemRemoved, notifyItemRangeRemoved - 특정 범의의 아이템 삭제
notifyItemMoved - 아이템 위치 이동
위의 기능들을 활용하여 RecyclerView를 유용하게 사용할 수 있다.
하지만 notifyDataSetChanged같은 경우는 리스트를 전부 지우고 처음부터 하나씩 view를 만들기 때문에 리소스가 굉장히 비효율적으로 사용 되는 단점이 있다.
그 외 다른 방법들도 리스트가 수정되는 케이스에 맞춰 적용하는 게 번거롭게 느껴진다.
DiffUtil은 이전 데이터와 현재 데이터를 비교하여 반드시 수정되어야 할 부분만 갱신하게 된다.
즉 번거롭게 리스트에 발생하는 케이스에 맞는 함수를 사용할 필요 없고, 데이터 업데이트도 최소한으로 하여 성능적인 부분에서도 큰 이점이 있다.
전에 작성했던 RecyclerView를 사용해 보자 프로젝트에 DiffUtil을 추가해보자
class DiffUtilCallback(
private val oldList: MutableList<RecyclerViewItem>,
private val newList: MutableList<RecyclerViewItem>
) : DiffUtil.Callback() {
// 이전 목록의 크기
override fun getOldListSize(): Int = oldList.size
// 변경된 목록의 크기
override fun getNewListSize(): Int = newList.size
/**
* 두 객체가 같은 아이템인지 비교함
* 보통 유니크 키 값을 사용하여 비교함
* 현재 프로젝트에선 item의 title로 비교
*/
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
oldList[oldItemPosition].title == newList[newItemPosition].title
/**
* areItemsTheSame 함수가 True이면 호출됨
* 두 개의 아이템이 같은 데이터를 가지고 있는지 '=='를 통해 동등성을 비교함
* 같은 아이템이라도 다른 값을 가지고 있는 경우가 발생함
*/
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
oldList[oldItemPosition] == newList[newItemPosition]
}
DiffUtil.Callback을 상속받고 오버라이딩 하여 함수를 사용하면 된다.
그리고 RecyclerViewAdapter에 DiffUtilCallback 객체를 생성해준다.
private val itemList = mutableListOf<RecyclerViewItem>()
fun updateList(items: MutableList<RecyclerViewItem>) {
val diffCallback = DiffUtilCallback(itemList, items)
val diffResult = DiffUtil.calculateDiff(diffCallback)
itemList.clear()
itemList.addAll(items)
diffResult.dispatchUpdatesTo(this)
}
updateList 함수를 만들고 새로 들어온 데이터와 기존에 있던 데이터를 DiffUtilCallback 객체에 넣어준다.
DiffUtil.calculateDiff에 생성한 객체를 넣어준 후 diffResult.dispatchUpdatesTo(this)를 호출하게 되면 RecyclerView를 갱신할 수 있게 된다.