드디어 마지막 단계까지 왔습니다. 앞에서 DiffUtil과 AsyncListDiffer에 대해 알아본 것은 ListAdapter을 사용하기 위함이었다고 할 정도로 앞의 내용을 모두 포함하고 있는 주제입니다.
입사 한 직후 안드로이드를 다시 시작해서 처음 들었던 주제를 드디어 다 정리하네요.
만약 AsyncListDiffer를 모르신다면 보고 오시는 거 추천드립니다. 이해하는데 100% 도움을 줍니다.
DiffUtil과 CallBack에 대해 알고 싶으신 분들은 DiffUtil 게시글을 보시는 걸 추천드려요
정의
공식 사이트에선
This class is a convenience wrapper around AsyncListDiffer that implements Adapter common default behavior for item access and counting.
라고 합니다.
간단하게 정리하면 이전 시간에 구현했던 AsyncListDiffer를 편리하게 쓰기 위한 래퍼 클래스(편하게 쓸 수 있도록 감싼 클래스)입니다. 차이점은 직접 submitList(), getCurrentList()를 구현할 필요가 없다는 것입니다.
메서드
리턴타입 | 이름 | 기능 |
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에 전달한다 |
구현
기본적인 리사이클러뷰 구현은 DiffUtil 게시글에서 확인할 수 있습니다.
구현해야 하는 것은 총 2가지입니다.
- DiffUtil.ItemCallBack
- ListAdapter
DiffUtilItemCallBack
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)
}
}
ListAdapter
class RecyclerListAdapter: ListAdapter<RCDto, RecyclerListAdapter.ListAdapterViewHolder>(cb) {
companion object{
val cb = DiffUtilItemCallBack()
}
inner class ListAdapterViewHolder(val binding: ViewholderMainBinding): RecyclerView.ViewHolder(binding.root){
fun bind(rcDto: RCDto){
Glide.with(itemView.context).load(rcDto.image).into(binding.vhImage)
binding.vhTitle.text = rcDto.title
binding.vhContent.text = rcDto.content
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListAdapterViewHolder = ListAdapterViewHolder(
ViewholderMainBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: ListAdapterViewHolder, position: Int) {
holder.bind(currentList[position])
}
}
AsyncListDiffer처럼 submitList와 같은 필요한 메서드를 구현할 필요 없이 ListAdapter의 생성자로 타입과 콜백을 넘겨주기만 하면 알아서 다 구현됩니다. 저희가 할 일은 뷰 홀더에 바인딩하는 코드만 짜주고 companion object로 콜백을 전달해주기만 하면 됩니다.
MainModel
private var rcListAdapter = RecyclerListAdapter()
//ListAdapter
fun initListAdapter(){
rcListAdapter.submitList(ArrayList(dataset))
binding.recycler.adapter = rcListAdapter
binding.recycler.layoutManager = LinearLayoutManager(cnx)
}
//ListAdapter
fun onClickEventByListAdapter(){
binding.addButton.setOnClickListener {
if(flag){
addData()
rcListAdapter.submitList(ArrayList(dataset))
flag = false
}
}
}
어댑터 생성 후 새 리스트를 submitList로 넘겨주면 됩니다. 변경사항이 있으면 마찬가지로 submitList로 새 리스트를 넘겨주면 됩니다.
MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var mainModel: MainModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewBinding()
initRecycler()
setOnClick()
}
private fun viewBinding(){
binding = ActivityMainBinding.inflate(layoutInflater)
mainModel = MainModel(this, binding)
setContentView(binding.root)
}
private fun initRecycler(){
mainModel.setData()
mainModel.initListAdapter()
}
private fun setOnClick(){
mainModel.onClickEventByListAdapter()
}
}
결과
잘 되는 것을 볼 수 있습니다.
이쯤 되면 드는 생각 하나
왜 구현할 필요 없이 submitList가 자동적으로 될까요?
public abstract class ListAdapter<T, VH extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<VH> {
final AsyncListDiffer<T> mDiffer;
private final AsyncListDiffer.ListListener<T> mListener =
new AsyncListDiffer.ListListener<T>() {
@Override
public void onCurrentListChanged(
@NonNull List<T> previousList, @NonNull List<T> currentList) {
ListAdapter.this.onCurrentListChanged(previousList, currentList);
}
};
@SuppressWarnings("unused")
protected ListAdapter(@NonNull DiffUtil.ItemCallback<T> diffCallback) {
mDiffer = new AsyncListDiffer<>(new AdapterListUpdateCallback(this),
new AsyncDifferConfig.Builder<>(diffCallback).build());
mDiffer.addListListener(mListener);
}
@SuppressWarnings("unused")
protected ListAdapter(@NonNull AsyncDifferConfig<T> config) {
mDiffer = new AsyncListDiffer<>(new AdapterListUpdateCallback(this), config);
mDiffer.addListListener(mListener);
}
public void submitList(@Nullable List<T> list) {
mDiffer.submitList(list);
}
public void submitList(@Nullable List<T> list, @Nullable final Runnable commitCallback) {
mDiffer.submitList(list, commitCallback);
}
protected T getItem(int position) {
return mDiffer.getCurrentList().get(position);
}
@Override
public int getItemCount() {
return mDiffer.getCurrentList().size();
}
@NonNull
public List<T> getCurrentList() {
return mDiffer.getCurrentList();
}
public void onCurrentListChanged(@NonNull List<T> previousList, @NonNull List<T> currentList) {
}
}
상속하고 있는 ListAdapter입니다. 보시면 AsyncListDiffer 할 때 구현했던 메서드들이 보일 겁니다.
이미 구현되어 있기 때문에 편하게 사용할 수 있는 겁니다.
이렇게 이번 게시글까지 해서 ListAdapter에 대해 알아봤습니다. RecyclerView를 사용하면서 아이템 업데이트가 필요하면 가장 많이 사용하는 클래스라 "자주 사용 == 확실히 알아야 한다"라는 생각으로 하나씩 정리해 봤습니다. 다음 게시글은 MVVM 아키텍처 패턴을 이용하여 RecyclerView를 만드는 걸로 찾아오도록 하겠습니다.
참고
'안드로이드 > RecyclerView' 카테고리의 다른 글
RecyclerView의 notifyItem메서드들의 문제점을 해결해보자! 2탄(AsyncListDiffer) (0) | 2023.03.21 |
---|---|
RecyclerView의 notifyItem메서드들의 문제점을 해결해보자! 1탄(DiffUtil) (0) | 2023.03.20 |
RecyclerViewItemClickEvent (0) | 2022.06.13 |
RecyclerView (0) | 2022.06.13 |