기존에 RecyclerView에서 데이터를 바꿀 때 매번 Adapter안에 setData() 메서드를 만들고 이 안에서 notifyDataSetChanged() 메서드를 호출하는 방식을 사용하였다. 하지만 이 방법은 데이터를 변경되지 않은 부분을 포함하여 전체를 다시 그리는 동작을 하기 때문에 데이터가 자주 바뀌는 환경에서 비용이 많이 든다는 단점이 있다.
ListAdapter를 사용하면 요소의 추가 및 삭제를 처리할 수 잇으며 변경사항에 애니메이션을 추가할 수 있다.
원래 리스트와 새로 들어온 리스트 간의 차이를 계산하고 DiffUtil.Callback 이라는 추상 클래스를 콜백 클래스로 활용하게 된다.
calculateDiff() 에서 diff 알고리즘을 통해 변경된 아이템을 감지한다.dispatcUpdatesTo()에서 지정된 Adapter로 업데이트 이벤트를 전달한다.public void dispatchUpdatesTo(@NonNull RecyclerView.Adapter adapter){
List oldList = mAdapter.getData();
DiffResult result = DiffUtil.calculateDiff(new MyCallback(oldList, newList));
mAdapter.setData(newList);
result.dispatchUpdatesTo(mAdapter);
}
DiffUtil.Callback 클래스를 구현하려면 총 네 가지의 메서드를 오버라이딩 해야 한다.
override fun getOldListSize(): Int {
TODO("Not yet implemented")
}
override fun getNewListSize(): Int {
TODO("Not yet implemented")
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
TODO("Not yet implemented")
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
TODO("Not yet implemented")
}
https://developer.android.com/reference/androidx/recyclerview/widget/AsyncListDiffer
DiffUtil의 경우 리스트의 아이템이 많으면 하나하나 모두 비교 연산을 수행하므로 작업 시간이 길어질 수 있다. 이를 위해 비교 연산을 백그라운드 스레드에서 처리하고자 한다.
public AsyncListDiffer(@NonNull RecyclerView.Adapter adapter,
@NonNull DiffUtil.ItemCallback<T> diffCallback) {
this(new AdapterListUpdateCallback(adapter),
new AsyncDifferConfig.Builder<>(diffCallback).build());
}
Adapter와 DiffUtil을 인자로 받아 DiffUtil을 백그라운드 스레드에서 수행하고 리사이클러뷰에 결과를 반영 할 수 있게 한다.
class ItemOrderUIModelDiff : DiffUtil.ItemCallback<ItemOrderUIModel>() {
override fun areItemsTheSame(oldItem: ItemOrderUIModel, newItem: ItemOrderUIModel): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: ItemOrderUIModel, newItem: ItemOrderUIModel): Boolean {
return oldItem == newItem
}
}
areItemsTheSame()을 먼저 수행하고 true일 때만 areContentsTheSame()을 수행한다.