지난 시간엔 DiffUtil을 이용해서 문제점을 해결했다면 이번 시간엔 AsyncListDiffer을 이용해서 문제를 해결해 보겠습니다. 이번 포스팅을 보고 나면 ListAdapter을 이해하는데 훨씬 수월합니다.
ListAdapter가 AsyncListDiffer의 래핑 클래스니까요
개념
DiffUtil이 두 리스트의 차이점을 비교할 때 백그라운드 스레드에서 작동하도록 도와주는 도우미라고 볼 수 있습니다.
리사이클러뷰 어댑터에 연결될 수 있고 제출된 리스트 간에 변경사항을 어댑터에게 알립니다.
단순화를 위해 ListAdapter를 사용할 수 있습니다. 래퍼 클래스라 문제없습니다.
전체 과정은 다음과 같습니다.
getCurrentList를 사용하여 현재 목록에 액세스 하고 해당 데이터 개체를 표시합니다. Diff 결과는 현재 목록이 업데이트되기 직전에 ListUpdateCallback으로 발송됩니다. (어댑터에 직접 목록 업데이트를 전달하는 경우 이는 어댑터가 getCurrentList를 통해 목록 항목과 전체 크기에 안전하게 액세스 할 수 있음을 의미합니다.)
AsyncListDiffer메서드
리턴타입 | 이름 | 기능 |
void | addListListener(@NonNull AsyncListDiffer.ListListener<T> listener) | 현재 리스트가 변경될 때 업데이트를 수신하기위한 ListListener 추가 |
@NonNull List<T> | getCurrentList() | 현재 리스트 가져오기 - 이 리스트를 표시하기 위한 모든 diff는 이미 계산되어 ListUpdateCallback을 통해 전달되었 |
void | removeListListener(@NonNull AsyncListDiffer.ListListener<T> listener) | 이전에 등록된 listListener제거 |
void | submitList(@Nullable List<T> newList) | 새 리스트를 AdapterHelper에 전달한다 |
void | submitList(@Nullable List<T> newList, @Nullable Runnable commitCallback) | 새 리스트를 AdapterHelper에 전달한다 |
구현
기본적인 리사이클러뷰 구현은 아래 게시글을 참고하면 됩니다.
ItemCallBack
이번에 사용할 콜백은 DiffUtil.ItemCallBack입니다. DiffUtil.CallBack과 다르게 아이템 객체가 있어서 편합니다.
class DiffUtilItemCallBack: DiffUtil.ItemCallback<RCDto>() {
override fun areItemsTheSame(oldItem: RCDto, newItem: RCDto): Boolean = oldItem === newItem
override fun areContentsTheSame(oldItem: RCDto, newItem: RCDto) = oldItem == newItem
override fun getChangePayload(oldItem: RCDto, newItem: RCDto): Any? {
return super.getChangePayload(oldItem, newItem)
}
}
areItemsTheSame에선 두 객체가 같은 주소인지 확인하기 때문에 값 비교( == )가 아닌 주소비교( === )를 사용했습니다.
AsyncListDifferAdapter
//가장 큰 차이점 생성자 프로퍼티가 필요없어짐
class AsyncListDifferAdapter: RecyclerView.Adapter<AsyncListDifferAdapter.MainViewHolder>() {
//AsyncListDiffer 정의
private var mDiffer: AsyncListDiffer<RCDto> = AsyncListDiffer(this, DiffUtilItemCallBack())
inner class MainViewHolder(view: View): RecyclerView.ViewHolder(view){
var binding: ViewholderMainBinding
init {
binding = ViewholderMainBinding.bind(view)
}
fun bind(rcDto: RCDto){
Glide.with(itemView.context)
.load(rcDto.image)
.into(binding.vhImage)
binding.apply {
vhTitle.text = rcDto.title
vhContent.text = rcDto.content
}
}
}
//리스트 변경
fun submitList(data: ArrayList<RCDto>) = mDiffer.submitList(data)
//아이템 가져옴
private fun getItem(position: Int): RCDto = mDiffer.currentList[position]
//뷰 그룹의 뷰를 뷰 홀더를 넣어줌
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.viewholder_main, parent, false)
return MainViewHolder(view)
}
//뷰 홀더에 데이터 셋팅
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
holder.bind(getItem(position))
}
override fun getItemCount() = mDiffer.currentList.size
}
이전에 구현했던 DiffUtil과 가장 큰 차이점은 기본 생성자 밖에 없다는 점입니다.
내부에서 AsyncListDiffer객체로 바인딩해 주고 리스트를 업데이트할 땐 mDiffer.submitList()로 업데이트해 줍니다.
다른 어댑터와 비교해서 추가된 메서드는
- 리스트 변경을 위한 submitList
- 아이템을 가져오기 위한 getItem
두 가지입니다.
MainModel 추가
private var asyncListDifferAdapter = AsyncListDifferAdapter()
fun initAsyncListDifferAdapter(){
asyncListDifferAdapter.submitList(ArrayList(dataset))
binding.recycler.adapter = asyncListDifferAdapter
binding.recycler.layoutManager = LinearLayoutManager(cnx)
}
//AsyncListDiffer를 이용한 업데이트
fun onClickEventByAsyncListDiffer(){
binding.addButton.setOnClickListener {
if(flag){
addData()
asyncListDifferAdapter.submitList(ArrayList(dataset))
flag = false
}
}
}
차이점을 하나 찾을 수 있습니다. 다른 어댑터들은 객체 생성 시 생성자에 데이터 리스트를 넘겨주는 구조였지만 AsyncListDiffer는 sumitList메서드를 이용해서 데이터 리스트를 넘겨주는 것을 볼 수 있습니다.
또한 sumitList에 파라미터를 넘겨줄 때 같은 주소값을 가지고 있으면 업데이트를 하지 않으므로 새 리스트로 넣어줘야 합니다.
결과
잘 작동되는 것을 볼 수 있습니다.
다음 게시글에선 백그라운드 스레드에서 데이터 리스트를 업데이트해 주는 AsyncListDiffer를 랩핑 한 ListAdapter로 RecyclerView 구현하는 것으로 찾아뵙겠습니다.
참고
'안드로이드 > RecyclerView' 카테고리의 다른 글
RecyclerView의 notifyItem메서드들의 문제점을 해결해보자! 3탄(ListAdapter) (0) | 2023.03.22 |
---|---|
RecyclerView의 notifyItem메서드들의 문제점을 해결해보자! 1탄(DiffUtil) (0) | 2023.03.20 |
RecyclerViewItemClickEvent (0) | 2022.06.13 |
RecyclerView (0) | 2022.06.13 |