-
[Android] RecyclerView ViewHolder ํจํด, DiffUtil ํด๋์คAndroid 2022. 3. 22. 02:52๋ฐ์ํ
RecyclerView
์์ ์๋๋ก์ด๋ ๊ฐ๋ฐํ๋ฉด์ ๋ฆฌ์คํธ ํํ๋ก ๋ฐ์ดํฐ๋ค์ ํํํด์ฃผ๋ ๋ฐ ์ฌ์ฉํ๋ ํด๋์ค๋ ListView ์์ต๋๋ค. ํ์ง๋ง ๋ฐ์ดํฐ์ ํฌ๊ธฐ๊ฐ ์ปค์ง์๋ก ๋ทฐ๋ฅผ ๊ณ์ ์์ฑํจ์ผ๋ก์จ ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ ํ์์ด ๋ฐ์ํ๊ฒ ๋๊ณ getView ๋ฉ์๋์์ ๋ฌด๋ถ๋ณํ findViewById๋ฅผ ์ฌ์ฉํ์๊ธฐ์ ๋นํจ์จ์ ์ด์์ต๋๋ค. ์ด๋ฅผ ๊ฐ์ ํ ์์ ฏ์ด RecyclerView๋ก ํ๋ฉด์ด ๋ณด์ฌ์ง๋ ๋ทฐ๊น์ง๋ง ์์ฑํ ํ ์คํฌ๋กค ์์ ๊ฐ๋ ค์ง๊ฒ ๋๋ ๋ทฐ๋ค์ ์ฌ์ฌ์ฉํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. ๋ํ, findViewById์ ๊ฐ ๋น์ผ ๋น์ฉ์ ๋ฐฉ์งํ๊ณ ์ ViewHolder ํจํด์ ๊ฐ์ ํ ์ํจ ListView๋ผ๊ณ ํ ์ ์์ต๋๋ค.
ViewHolder ํจํด์ด๋?
ViewHolder ํจํด์ ์ดํดํ๊ธฐ ์ํด์๋ ViewHolder ํจํด์ด ์ ํ์ํ์ง๋ถํฐ ์ดํดํ๋ฉด ์ข์ต๋๋ค.
class ListAdapter(private val items : List<String>): BaseAdapter() { override fun getCount(): Int = items.size override fun getItem(position: Int): String = items.get(position) override fun getItemId(position: Int): Long = position.toLong() override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { var view = convertView if(view == null){ view = LayoutInflater.from(parent?.context).inflate(R.layout.item_list, parent, false) } view?.findViewById<TextView>(R.id.textView).text = items.get(position) return view!! } }
์์ ์ฝ๋๋ ListView์ ์ด๋ํฐ์ธ ListAdapter ํด๋์ค๋ก getView๋ฉ์๋๋ position์ ํด๋นํ๋ View๋ฅผ ๋ฐํํ๋ ๋ฉ์๋์ ๋๋ค. getView ๋ฉ์๋์ ํ๋ผ๋ฏธํฐ๋ก convertView๊ฐ ์กด์ฌํ๋๋ฐ convertView๋ ์ฌ์ฌ์ฉ๋๋ ๋ทฐ๋ก์ ListView๊ฐ ์คํฌ๋กคํ์ฌ ์ด์ ์ ์์ฑํ๋ ๋ทฐ๊ฐ ๊ฐ๋ ค์ก์ ๋ ํด๋น ๋ทฐ๋ก ์ฌ์ฌ์ฉํ๊ฒ ๋ฉ๋๋ค. ์ฆ, ๋ณด์ฌ์ฃผ๊ณ ์ ํ๋ ๋ชจ๋ ๋ฐ์ดํฐ ๊ฐฏ์๋งํผ ๋ทฐ๋ฅผ ์์ฑํ๋ฉด ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ญ๋นํ๊ฒ ๋๋ฏ๋ก ํ๋ฉด์ ๋ณด์ฌ์ง๋ ๋ทฐ๋งํผ๋ง ์์ฑํ๊ณ ์ฌ์ฌ์ฉํ๋ค๋ ๊ฒ์ ๋๋ค.
๋ทฐ๋ฅผ ์ฌ์ฌ์ฉํ์ฌ ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น๋ ๋ฐฉ์งํ์์ง๋ง getView ๋ฉ์๋๋ง๋ค findViewById๊ฐ ํธ์ถ๋ฉ๋๋ค. ๊ฐ๋จํ ๋ทฐ๋ฉด ํฌ๊ฒ ๋ฌธ์ ๋์ง ์์ง๋ง ๋ณต์กํ ๋ทฐ์ผ์๋ก findViewById์ ํธ์ถ๋ ๋ง์์ง๊ณ findViewById์ ๋น์ฉ๋ ์ปค์ง๊ฒ ๋ฉ๋๋ค. findViewById์ ์๋ฆฌ๊ฐ ๊ถ๊ธํ์๋ฉด ์๋ ๋งํฌ๋ฅผ ํตํด ํ์ธํ์ค ์ ์์ต๋๋ค.
https://math-coding.tistory.com/251
class CustomAdapter(private val dataSet: Array<String>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() { /** * Provide a reference to the type of views that you are using * (custom ViewHolder). */ class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val textView: TextView init { // Define click listener for the ViewHolder's View. textView = view.findViewById(R.id.textView) } } // Create new views (invoked by the layout manager) override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder { // Create a new view, which defines the UI of the list item val view = LayoutInflater.from(viewGroup.context) .inflate(R.layout.text_row_item, viewGroup, false) return ViewHolder(view) } // Replace the contents of a view (invoked by the layout manager) override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { // Get element from your dataset at this position and replace the // contents of the view with that element viewHolder.textView.text = dataSet[position] } // Return the size of your dataset (invoked by the layout manager) override fun getItemCount() = dataSet.size }
์์ ์ฝ๋๋ Android ๊ณต์๋ฌธ์์ ์๋ ์ฝ๋๋ก ์ถ์ ํด๋์ค์ธ RecyclerView.Adpater๋ฅผ ์์๋ฐ์ CustomAdapter ํด๋์ค์ ๋๋ค.
CustomAdapter ๋ด์๋ ๋ด๋ถ ํด๋์ค์ธ Viewholder ํด๋์ค๊ฐ ์กด์ฌํ๋๋ฐ RecyclerView.Adapater๋ฅผ ์์๋ฐ๊ธฐ ์ํด์๋ RecyclerView.ViewHolder๊ฐ ์ ์๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์ Viewholder ํด๋์ค๋ฅผ ์์ฑํ์์ต๋๋ค. ์ด Viewholder ํด๋์ค๊ฐ findViewByid์ ๋จ์ ์ ๊ฐ์ ์ํฌ ์ ์๋ ๋ฐฉ๋ฒ์ผ๋ก ํด๋น ํด๋์ค์์ ๋ ์ด์์์ ์กด์ฌํ๋ ๋ชจ๋ ๋ทฐ๋ฅผ ์ ์ธํ๊ณ ํ ๋น๊น์ง ํ ์ํ๋ก View๋ฅผ ๊ฐ์ง๊ณ ์๊ฒ ๋ฉ๋๋ค.
๋จผ์ RecyclerView์ ๋์ ์์๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
- ํ๋ฉด์ ๋ณด์ฌ์ง ๋ทฐ๋ณด๋ค 1~2๊ฐ ๋ ๋ง๊ฒ ๋ทฐํ๋๋ฅผ ์์ฑํฉ๋๋ค.(onCreateViewHolder)
- ๊ฐ ๋ทฐํ๋์ position์ ํด๋นํ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ธ๋ฉํฉ๋๋ค.(onBindViewHolder)
- ์คํฌ๋กค ์์ ๊ฐ๋ ค์ ธ์ ๋ณด์ด์ง ์์ ๋ทฐํ๋์ ์๋ก์ด position์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ธ๋ฉํฉ๋๋ค.
RecyclerView๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ฌ์ฉ ๋ทฐ๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋๋ฐ getView ๋ฉ์๋์ ์ฐจ์ด์ ์ findViewById๋ฅผ ํ์ง ์์๋ ๋ ์ด์์ ๋ด์ ์กด์ฌํ๋ ๋ชจ๋ ๋ทฐ์ ๋ํ ์ฐธ์กฐ๊ฐ ๋์ด ์์ด์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ธ๋ฉ๋ง ์ํค๋ฉด ๋ฉ๋๋ค. ์ด๋ก ์ธํด findViewById๋ onCreateViewHolder๊ฐ ํธ์ถ๋ ์๋งํผ๋ง ํธ์ถ๋๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ ๊ฐ์ ์ด ๋์๋ค๊ณ ๋ณผ ์ ์์ต๋๋ค. ๐
๋ฐ์ํnotifyDataSetChanged
์์ ๋ด์ฉ๋ง ๋ณผ ๋์ ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น๋ ๊ฐ์ ํ์๊ณ findViewById๋ ๊ฐ์ ํ์ฌ์ RecyclerView๋ ๋ฌธ์ ์ ์ด ๋ชจ๋ ๊ฐ์ ์ด ๋์๋ค๊ณ ๋ณผ ์ ์์ต๋๋ค. ํ์ง๋ง RecyclerView์์๋ ๋นํจ์จ์ ์ธ ๊ตฌ์กฐ๊ฐ ์กด์ฌํ๋๋ฐ ๊ทธ๊ฒ์ ๋ฐ๋ก ์๋ก์ด ๋ฐ์ดํฐ ๋ฆฌ์คํธ๋ค์ ์ ์ฉํ ๋ ์ ๋๋ค. RecyclerView๋ฅผ ํ ๋ฒ์ด๋ผ๋ ์ฌ์ฉํ์ ๋ถ๋ค์ notifyDataSetChanged() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ๊ธฐ์กด์ ๋ณด์ฌ์คฌ๋ ๋ฆฌ์คํธ๋ฅผ ๊ฐฑ์ ์์ผ ์๋ก์ด ๋ฐ์ดํฐ๋ค๋ก ๋ณด์ฌ์ค๋ค๋ ๊ฒ์ ์๊ณ ์์ ๊ฒ์ ๋๋ค.
public void notifyChanged() { // since onChanged() is implemented by the app, it could do anything, including // removing itself from {@link mObservers} - and that could cause problems if // an iterator is used on the ArrayList {@link mObservers}. // to avoid such problems, just march thru the list in the reverse order. for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); } }
notifyDataSetChanged() ๋ฉ์๋ ๋ด๋ถ์์ notifyChanged ๋ฉ์๋๊ฐ ํธ์ถ๋๋๋ฐ ๋ชจ๋ ๋ฐ์ดํฐ๋ค์ ํ๋์ฉ ๋ณ๊ฒฝ์ ํ๊ณ ์์ต๋๋ค. ์ฆ, ๊ธฐ์กด์ ๋ฐ์ธ๋ฉ ํ๋ ๋ฐ์ดํฐ๋ค์ ์ง์ฐ๊ณ ๋ค์ ์ฒ์๋ถํฐ ๋ฐ์ธ๋ฉํ๊ณ ๋ ๋๋ง ํ๋ ๊ณผ์ ์ ๊ฑฐ์น๊ฒ ๋ฉ๋๋ค. ๋ง์ฝ 100๊ฐ์ ๋ฐ์ดํฐ ์ค 1๊ฐ์ ๋ฐ์ดํฐ ๊ฐ๋ง ๋ณ๊ฒฝํ์ง๋ง ์ ์ฒด ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ธ๋ฉํ๊ณ ๋ ๋๋ง ๊ณผ์ ์ ๊ฑฐ์น๋ค๋ฉด ๋งค์ฐ ๋นํจ์จ์ ์ธ ๊ตฌ์กฐ๊ฐ ๋๊ฒ ์ฃ ? ์ด๋ฅผ ๊ฐ์ ์์ผ์ค ์ ์๋ ๋ฐฉ๋ฒ์ด DiffUtil์ ์ด์ฉํ๋ ๊ฒ์ ๋๋ค.
DiffUtil
์์ ๊ฐ์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ DiffUtil ํด๋์ค๋ Eugene W.Myers's difference algorithm์ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ์ ๋ณ๊ฒฝ๋ ๋ถ๋ถ๋ง ์ ๋ฐ์ดํธํ๋๋ก ๋์ด ์์ต๋๋ค.
์์ ์ฝ๋๋ DiffUtil.Callback ์ถ์ํด๋์ค๋ก 4๊ฐ์ ์ถ์๋ฉ์๋์ 1๊ฐ์ ๋ฉ์๋๊ฐ ์กด์ฌํฉ๋๋ค.
- getOldListSize : ๊ธฐ์กด์ ์๋ ๋ฆฌ์คํธ์ ํฌ๊ธฐ ๋ฐํ
- getNetListSize : ๋ณ๊ฒฝ๋ ๋ฆฌ์คํธ์ ํฌ๊ธฐ ๋ฐํ
- areItemsTheSame : ๋ ๊ฐ์ ๊ฐ์ฒด๊ฐ ๊ฐ์์ง ํ์ธ, ์ฃผ์๊ฐ์ผ๋ก ๋น๊ตํ๊ฑฐ๋ ๊ณ ์ ์ ๊ฐ(ID)์ ํตํด ๋น๊ต
- areContentsTheSame : ๋ ๊ฐ์ ๋ฐ์ดํฐ๊ฐ ๊ฐ์์ง ํ์ธ
- getChangePayLoad : ๋ณ๊ฒฝ ๋ด์ฉ์ ๋ํ ํ์ด๋ก๋๋ฅผ ๊ฐ์ ธ์ด
์ ๋ฉ์๋์์ ์ค์ํ ๊ฒ์ areItmesTheSame๊ณผ areContentsTheSame ๋ฉ์๋๋ฅผ ๊ตฌ๋ณํ ์ค ์์์ผ ํ๋๋ฐ areItemsTheSame์ ๊ฐ์ฒด๊ฐ ๊ฐ์์ง ํ๋จํด์ผ ํ๊ธฐ ๋๋ฌธ์ ์ฃผ์๊ฐ์ผ๋ก ๋น๊ตํ๊ฑฐ๋ ๊ฐ์ฒด ๋ด ๊ณ ์ ์ ๊ฐ์ผ๋ก ๋น๊ต๋ฅผ ํด์ผ ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ areItemsTheSame ๋ฉ์๋๊ฐ true๋ฅผ ๋ฐํํด์ผ areContentTheSame ๋ฉ์๋๋ฅผ ํธ์ถํฉ๋๋ค. ๋น์ฐํ๋ฏ์ด ๊ฐ์ฒด๊ฐ ๋ค๋ฅด๋ฉด ๊ฐ์ฒด ๋ด ๋ฐ์ดํฐ๋ฅผ ํ์ธํ ํ์๋ ์๊ณ ๊ฐ์ฒด๋ ๊ฐ๋๋ผ๋ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฅธ ๊ฐ์ผ๋ก ์ธํ ํ ์ ์์ด์ areContentTheSame ๋ฉ์๋๋ก ํ์ธํฉ๋๋ค. ๊ฒฐ๋ก ์ ์ผ๋ก areItemsTheSame๊ณผ areContentsTheSame ๋ฉ์๋๊ฐ ๋ชจ๋ true์ด๋ฉด ๊ฐ์ ์์ดํ ์ผ๋ก ํ๋จํ์ฌ ์ ๋ฐ์ดํธ ํ์ง ์๊ฒ ๋ฉ๋๋ค.
class DiffUtilCallback( private val oldList: List<Book>, private val newList: List<Book> ): DiffUtil.Callback() { override fun getOldListSize(): Int = oldList.size override fun getNewListSize(): Int = newList.size override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { return oldList.get(oldItemPosition).id == newList.get(newItemPosition).id } override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { oldList.get(oldItemPosition) == newList.get(newItemPosition) } }
์์ ์ฝ๋์ฒ๋ผ DiffUtilCallback ํด๋์ค๋ฅผ ์ ์ํ๊ณ Adapater ๋ด๋ถ์ changeList๋ผ๋ ๋ฉ์๋๋ฅผ ๋ง๋ค์ด์ ์๋ก์ด ๋ฆฌ์คํธ ์ ๋ฐ์ดํธ ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋จผ์ DiffUtilCallback์ผ๋ก ์ด์ ๋ฆฌ์คํธ์ ์๋ก์ด ๋ฆฌ์คํธ์ Diff๋ฅผ ๊ณ์ฐํ ํ dispatchUpdatesTo ๋ฉ์๋๋ฅผ ํตํด Adapter์ ๋ณ๊ฒฝ๋ ๋ถ๋ถ์ ์๋ ค์ค ์ ์๊ฒ ๋ฉ๋๋ค.
fun changeList(books: List<Book>){ val diffUtilCallback = DiffUtilCallback(this.books,books) val diffResult = DiffUtil.calculateDiff(diffUtilCallback) this.books.apply { clear() addAll(books) diffResult.dispatchUpdatesTo(this@BookListAdapter) } }
AsyncListDiffer
DiffUtil.Callback ์ถ์ํด๋์ค๋ฅผ ๊ตฌํํ๋ ํด๋์ค๋ฅผ ์ง์ ๋ง๋ค์ด์ RecyclerView Adpater์์ ์ฌ์ฉํด๋ ์ข์ง๋ง ๋ฐ์ดํฐ๊ฐ ๋ง์์ง ์๋ก Eugene W.Myers's difference algorithm์ ์๊ฐ๋ณต์ก๋๊ฐ ์ปค์ง๊ธฐ์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์ข์ต๋๋ค. AsyncListDiffer๋ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ ๋ฆฌ์คํธ์ ๋ณ๊ฒฝ์ฌํญ์ ๊ณ์ฐํ๊ณ ์ ๋ฐ์ดํธ๊น์ง ์งํ์ ์์ผ์ฃผ๊ธฐ ๋๋ฌธ์ ํจ์จ์ ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋จผ์ DiffUtil.ItemCallback ํด๋์ค๋ฅผ ๋ง๋ญ๋๋ค.
DiffUtil.Callback๊ณผ ๋ค๋ฅด๊ฒ getOldListSize์ getNewListSize ๋ฉ์๋๋ ๋ฉ์๋๋ก ์กด์ฌํ์ง ์๊ธฐ ๋๋ฌธ์ areItemsTheSame๊ณผ areContentsTheSame๋ง ์ค๋ฒ๋ผ์ด๋ฉ ํ์์ต๋๋ค.
class DiffUtilCallback(): DiffUtil.ItemCallback<Book>(){ override fun areItemsTheSame(oldItem: Book, newItem: Book): Boolean { return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: Book, newItem: Book): Boolean { return oldItem == newItem } }
๊ทธ๋ฆฌ๊ณ Adapter์ AsyncListDiffer ๊ฐ์ฒด๋ฅผ ์ ์ธํ ๋ค changeList ๋ด๋ถ์ submitList๋ฅผ ํธ์ถ๋ง ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
์ฌ๊ธฐ์๋ง ๋ฉ์ถ์ง ์๊ณ AsyncListDiffer ์ submitList๋ฅผ ๋ถ์ํด๋ด ์๋ค. ์๋ ์ฝ๋๋ AsnycListDiffer ํด๋์ค์ submitList ๋ฉ์๋์ ์ผ๋ถ๋ก newList์ mList(์ด์ ๋ฆฌ์คํธ)์ ๋น๊ต๋ฅผ ํ๊ฒ ๋ฉ๋๋ค. ์ฌ๊ธฐ์ ์ค์ํ ์ ์ด ์๋๋ฐ newList์ mList์ ์ฐธ์กฐ๊ฐ ๊ฐ์ผ๋ฉด ๋ณ๊ฒฝ์ฌํญ์ ์ฒดํฌํ์ง๋ ์๊ณ ์ข ๋ฃ์ํต๋๋ค. ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๋์ ํ ๋ ์๋ฒ๋ DB๋ก๋ถํฐ ์๋ก์ด ๋ฆฌ์คํธ๋ฅผ ์์ฑํ๊ณ ๋ฐ์ดํฐ๋ฅผ ์ธํ ํ ํ submitList๋ฅผ ํ๊ฒ ๋๋ฉด ๋ฌธ์ ์์ง๋ง, ๊ธฐ์กด์ ์๋ ๋ฐ์ดํธ๋ฅผ ๊ฐ์ง๊ณ ์์๋ก ์ฝ๊ฐ์ ์์ ์ ํ ํ submitList๋ฅผ ํ๋ฉด ๊ฒฐ๊ตญ ๊ฐ์ ์ฐธ์กฐ๊ฐ์ ๊ฐ๋ฆฌํค๊ธฐ ๋๋ฌธ์ ์ ๋ฐ์ดํธ๊ฐ ์๋๋ ํ์์ด ๋ฐ์ํฉ๋๋ค.
์ดํ getBackgroundThreadExecutor๋ฅผ ํตํด ๋ฐฑ๊ทธ๋ผ์ด๋์ ์์ ์ ์คํํ๊ณ DiffUtil.Callback์ ์ ์ํฉ๋๋ค. getOldListSize์ getNewListSize ๋ฉ์๋ ๋ด๋ถ์ ์ผ๋ก ๋ฆฌ์คํธ๋ก ๋ฐํํ๊ฒ ๋๊ณ areItemsTheSame ๊ณผ areContentsTheSame์ DiffUitl.ItemCallback์์ ์ ์ํ ํด๋์ค๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ๊ณ ์์ต๋๋ค. ์ ๋ฆฌํ์๋ฉด DiffUtil.Callback ๋ฉ์๋๋ฅผ ๋์ผํ๊ฒ ์ฌ์ฉํ๋ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ ๋ณ๊ฒฝ์ฌํญ์ ๊ณ์ฐํ๋ค๊ณ ๋ณด๋ฉด ๋ฉ๋๋ค.
ListAdapter
RecyclerView์ notifyDataSetChanged ๋ฅผ ๊ฐ์ ํ๊ธฐ ์ํด์ DiffUtil.Callback ์ถ์ํด๋์ค๋ฅผ ๊ตฌํํ๊ฑฐ๋ AsyncListDiffer ํด๋์ค๋ฅผ ์ฌ์ฉํ์์ต๋๋ค. ํ์ง๋ง ๊ตฌ๊ธ์์ ๋๋์ฑ ์ฝ๊ฒ ์ฌ์ฉํ๋ผ๊ณ AsyncListDiffer ๋ฅผ ๋ํํ ListAdapter๋ฅผ ๋ง๋ค์์ต๋๋ค.
class BookAdapter(private val itemClickedListener: (Book) -> Unit) : ListAdapter<Book, BookAdapter.BookItemViewHolder>(diffUtil) { inner class BookItemViewHolder(private val binding: ItemBookBinding) : RecyclerView.ViewHolder(binding.root){ fun bind(bookModel: Book){ binding.titleTextView.text = bookModel.title binding.descriptionTextView.text = bookModel.description binding.root.setOnClickListener { itemClickedListener(bookModel) } Glide .with(binding.coverImageView.context) .load(bookModel.converSmallUrl) .into(binding.coverImageView) } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookItemViewHolder { return BookItemViewHolder(ItemBookBinding.inflate(LayoutInflater.from(parent.context),parent,false)) } override fun onBindViewHolder(holder: BookItemViewHolder, position: Int) { holder.bind(currentList[position]) } companion object { val diffUtil = object : DiffUtil.ItemCallback<Book>() { override fun areItemsTheSame(oldItem: Book, newItem: Book): Boolean { return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: Book, newItem: Book): Boolean { return oldItem == newItem } } } }
์ ์ฝ๋๋ ListAdpater๋ฅผ ํ์ฅํ BookAdpater๋ก ListAdpater์ ์ ๋๋ฆญ ํ์ ์ผ๋ก ๋ฆฌ์คํธ ์์ดํ ํ์ ์ธ Book๊ณผ ๋ด๋ถ ํด๋์ค๋ก ์ ์ํ BookItemViewHolder๋ฅผ ์ ์ธํ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ์์ฑ์ ๋งค๊ฐ๋ณ์๋ก DiffUtil.ItemCallback ํด๋์ค๋ฅผ ๋๋ฐ ๊ฐ์ฒด๋ก ์ ์ํ ๋ณ์๊ฐ์ ๋์ ํ์์ต๋๋ค. ListAdapter์์๋ ๋ด๋ถ ์์ฑ ๊ฐ์ผ๋ก AsyncListDiffer๊ฐ ์กด์ฌํ๊ณ ๋งค๊ฐ๋ณ์๋ก ๋์ ๋ DiffUtil.ItemCallback ํด๋์ค๋ฅผ ์ด์ฉํ์ฌ submitList ์ ๋ณ๊ฒฝ์ฌํญ์ ์ฒดํฌํ๊ณ ์ ๋ฐ์ดํธ ์์ผ์ฃผ๊ฒ ๋ฉ๋๋ค. ๐
ํ์ง๋ง ์์ง๊น์ง ListAdapter์ ์ด์๊ฐ ์กฐ๊ธ ๋ง์ ๊ฒ์ฒ๋ผ ๋ณด์ ๋๋ค. ๋ณ๊ฒฝ ๋ด์ญ์ ๊ณ์ฐํ๊ธฐ๊น์ง ์๊ฐ์ด ์์๋์ด ์ ๋ฐ์ดํธ ์ ๋๊น์ด ๋ฐ์ํ๋ค๊ฑฐ๋, scroll position์ด ์๋์ผ๋ก ๋งจ ๋ง์ง๋ง์ผ๋ก ์ด๋ํ๋ ์ด์๋ฅผ ๊ฒช์ด๋ณธ์ ์ด ์์ต๋๋ค. scrollToPosition๋ ์ ์์ ์ผ๋ก ์์ ์ด ๋์ง ์์ submitList(null) ์ดํ submitList(newList)๋ฅผ ํธ์ถํ์ฌ ์์ ํ์์ต๋๋ค. ์ด๋ฌ๋ฉด ListAdpater๋ฅผ ์ฐ๋ ์๋ฏธ๊ฐ ์๊ธด ํ๋ฐ... ํด๊ฒฐํ์ ์ ๋ฐฐ๋๋ค ์์ผ์๋ฉด ๋๊ธ ๋จ๊ฒจ์ฃผ์ธ์.๐
์ฐธ๊ณ
๋ฐ์ํ'Android' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Android] ViewBinding์ด๋? (0) 2022.03.20 [Android] Storage Access Framework(SAF) ์ด๋? (0) 2022.03.13 [Android] findViewById ์๋ฆฌ (0) 2022.03.13 [Android] Native Application(C/C++), NDK build ๋ฐ CMake ๊ตฌ์ฑ (0) 2022.02.14 [Android] ContentProvider ๊ตฌํ ๋ฐ ์ฌ์ฉ๋ฒ (0) 2022.02.05