드디어 마지막 단계까지 왔습니다. 앞에서 DiffUtil과 AsyncListDiffer에 대해 알아본 것은 ListAdapter을 사용하기 위함이었다고 할 정도로 앞의 내용을 모두 포함하고 있는 주제입니다.
입사 한 직후 안드로이드를 다시 시작해서 처음 들었던 주제를 드디어 다 정리하네요.
만약 AsyncListDiffer를 모르신다면 보고 오시는 거 추천드립니다. 이해하는데 100% 도움을 줍니다.
RecyclerView의 notifyItem메서드들의 문제점을 해결해보자! 2탄(AsyncListDiffer)
지난 시간엔 DiffUtil을 이용해서 문제점을 해결했다면 이번 시간엔 AsyncListDiffer을 이용해서 문제를 해결해 보겠습니다. 이번 포스팅을 보고 나면 ListAdapter을 이해하는데 훨씬 수월합니다. ListAdapte
huzit.tistory.com
DiffUtil과 CallBack에 대해 알고 싶으신 분들은 DiffUtil 게시글을 보시는 걸 추천드려요
RecyclerView의 notifyItem메서드들의 문제점을 해결해보자! 1탄(DiffUtil)
RecyclerView RecyclerView란? 1. 정의 안드로이드 공식문서에는 A flexible view for providing a limited window into a large data set.로 소개하고 있습니다. 번역하면 '대규모 데이터셋을 제한된 범위에 제공하기 위한
huzit.tistory.com
정의
공식 사이트에선
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를 만드는 걸로 찾아오도록 하겠습니다.
참고
GitHub - Huzit/RecyclerViewExample
Contribute to Huzit/RecyclerViewExample development by creating an account on GitHub.
github.com
ListAdapter | Android Developers
androidx.constraintlayout.core.motion.parse
developer.android.com
'안드로이드 > 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 |