나는 b를 삭제했는데 c가 삭제된 것처럼 보이는 매직
나는 b를 지웠는데 c가 삭제된 것처럼 보인다. 하지만 나갔다 들어오니 b가 지워진게 맞다;
코드는 아이템이 변경되었을 때 notifyDataSetChanged()를 호출하여 UI가 갱신되도록 구현하였다.
하지만 해당 함수 사용이 부적절함을 깨달았다.
리사이클러뷰는 어댑터의 메소드를 통해 아이템 변경을 감지하고 갱신할 수 있다.
그러나 정확히 어떤 아이템이 변경되었는지는 알 수 없기 때문에 변경된 아이템의 position을 알려줘야 한다.
notifyDataSetChanged
아이템 변경(데이터가 업데이트 되었지만 위치는 변하지 않았을 때), 구조적 변경(아이템간에 삽입, 삭제, 이동이 일어났을 때)에 사용한다.
notifyItemChanged
notifyItemChanged(int position, Object payload)
position 위치의 아이템이 변경되었다고 파라미터를 통해 알려줄 수 있다.
notifyItemInserted
notifyItemInserted(int position)
position 위치에 아이템이 추가되었다는 뜻이다.
notifyItemMoved
notifyItemMoved(int fromPostion, int toPosition)
인덱스 fromPosition 아이템이 toPosition으로 이동하였다.
notifyItemRangeChanged
notifyItemRangeChanged(int positionStart, int itemCount, Object payload)
positionStart부터 itemCount개까지 범위에서 변경이 일어났다.
이를 해결하기 위해
temporalList.addAll(adapterList); // 임시 리스트에 Adapter에 연결한 list의 내용을 모두 담고
temporalList.add(newItem); // 새로운 아이템을 등록한 후에
adapterList.clear(); // adapter에 연결한 list의 내용을 모두 지웠다가
adapterList.addAll(temporalList); // 임시리스트의 내용을 모두 adapter에 연결한 리스트에 추가하기
adapter.notifyDataSetChanged(); // 그리고 notifyDataSetChanged를 호출해보세요.
이런 방법도 있는 듯 하나 효율적으로 보이지 않는다.
1000개 중 하나만 변경되었더라도 전부 다 지웠다가 다시 add해야 하기 때문이다.
그래서 아이템 변경을 감지하고 갱신하는 역할을 DiffUtil에게 위임하여 문제점을 해결하였다.
DiffUtil은 oldList와 newList를 비교하여 차이를 계산하고, newList로 갱신해주는 유틸리티 클래스이다.
즉, 이 클래스를 사용하면 아이템 변경의 구체적인 상황에 따라 Adapter의 적절한 메소드를 호출하지 않아도 된다.
class ContactDiffUtil(private val oldList: List<Contact>, private val currentList: List<Contact>):DiffUtil.Callback(){
override fun getOldListSize(): Int =oldList.size
override fun getNewListSize(): Int =currentList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id==currentList[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition]==currentList[newItemPosition]
}
}
총 4개 메소드를 오버라이드 해줘야 한다. 메소드는 이름명과 리턴값을 보면 어떤 역할을 하는지 쉽게 예측할 수 있다.
위와 같이 클래스를 하나 정의하고, 원래 notifySetDataChanged()를 호출할 곳에 아래 코드로 대체하면 된다.
fun setContact(contacts: List<Contact>){
val diffResult=DiffUtil.calculateDiff(ContactDiffUtil(this.contacts, contacts), false)
diffResult.dispatchUpdatesTo(this)
this.contacts=contacts
}
코드의 뜻은,
1. calculateDiff()로 oldList와 newList의 차이를 계산한다.
2. 차이 값을 업데이트하고, (notify~ 기능와 같다고 보면 된다).
3. list가 갱신되었으므로 기존 this.contacts를 newList인 contacts로 업데이트한다.