Android[kotlin] - ListAdapter, DiffUtil

안지현·2023년 3월 28일
0

Android[kotlin]

목록 보기
3/8

Adapter의 역할

데이터를 받아오고 이를 레이아웃에 직접 연결하는 함수를 실행시키는 클래스
= ListView나 App의 특정 데이터와 RecyclerView의 view를 Bind하는 것을 도와준다.

  • 미리 생성해둔 뷰홀더 객체에 사용자가 원하는 데이터 리스트를 주입하고 데이터 리스트에 변경사항이 있을 때 이를 UI에 반영한다.

하지만 기존에 notifyDataSetChanged()를 사용하면 리스트 내의 데이터가 바뀌면 리스트 전체가 바뀌었음 -> 지연 시간 발생(Flickering Issue)

이런 현상을 막고자 DiffUtil 이 나왔다.

DiffUtil

두 목록 간의 차이를 계산하고 첫 번째 목록을 두 번째 목록으로 변환하는 업데이트 작업 목록을 출력하는 유틸리티 클래스

  • RecyclerView 어댑터의 업데이트를 계산하는 데 사용할 수 있다.
  • ListAdapter를 통해 백그라운드 Thread에서 DiffUtil 사용을 단순화할 수 있다.

유형

  • abstract class DiffUtil.CallBack : 두 목록 간의 차이를 계산하는 동안 DiffUtil에서 사용하는 콜백 클래스
  • class DiffUtil.DiffResult : 호출 결과에 대한 정보 유지하는 클래스
  • abstract class DiffUtil.ItemCallback<T> : 목록에서 null이 아닌 두 항목 간의 차이를 계산하기 위한 콜백
 //diffutil -> 새로운 아이템 할당할지 말지 판단해주는 ->콜백 구현해야함
companion object{
        val diffUtil=object:DiffUtil.ItemCallback<Book>(){
        
            // 두 아이템이 동일한 아이템인지 체크. 보통 고유한 id를 기준으로 비교
            override fun areItemsTheSame(oldItem: Book, newItem: Book): Boolean {
               return oldItem.id==newItem.id
            }
            
	// 두 아이템이 동일한 내용을 가지고 있는지 체크. areItemsTheSame()이 true일때 호출됨
            override fun areContentsTheSame(oldItem: Book, newItem: Book): Boolean {
                return oldItem==newItem
            }


        }

ListAdapter

abstract class ListAdapter<T, VH: RecyclerView.ViewHolder?> : RecyclerView.Adapter

RecyclerView.Adapter 백그라운드 Thread에서 목록 간의 차이 계산을 포함하여 목록 데이터에 표시하기 위한 기본 클래스.
항목 액세스 및 계산을 위한 어댑터 공통 기본 동작을 구현하는 편리한 래퍼.

  • ListAdapter<데이터클래스, 리사이클러뷰 뷰홀더>를 인자로 받음. 어댑터 내에서 리스트를 정의하는 것이 아니라 리스트 자체에서 데이터 리스트를 정의하기 때문
  • 그래서 일반적인 RecyclerViewAdapter와 다르게 getItemCount() 구현 안 해도 됨

ListAdapter에서 사용할 수 있는 메서드

  1. getCurrentList() : 현재 리스트를 반환한다.
  2. onCurrentListChanged() : 리스트가 업데이트 되었을 때 실행할 콜백을 지정할 수 있다.
  3. submitList(list) : 리스트 데이터를 교체할 때 사용한다.

소스 코드

FarmRVAdapter.kt

class FarmRVAdapter: ListAdapter<RVFarmDataModel, FarmRVAdapter.ViewHolder>(diffUtil) {

    inner class ViewHolder(private val binding: RvLocalFarmBinding): RecyclerView.ViewHolder(binding.root){

        fun bind(item : RVFarmDataModel){
            binding.rvFarmImage.setImageResource(item.farm_image)
            binding.rvFarmName.text = item.farm_name
            binding.rvFarmSize.text = item.farm_size
            binding.rvFarmPrice.text = item.farm_price
            binding.bookMark.setOnClickListener {
                binding.bookMark.isSelected = !binding.bookMark.isSelected
            }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FarmRVAdapter.ViewHolder {

        return ViewHolder(RvLocalFarmBinding.inflate(LayoutInflater.from(parent.context), parent, false))
    }

    override fun onBindViewHolder(holder: FarmRVAdapter.ViewHolder, position: Int) {

        holder.bind(currentList[position])

    }

    companion object{
        val diffUtil = object: DiffUtil.ItemCallback<RVFarmDataModel>(){
            override fun areItemsTheSame(
                oldItem: RVFarmDataModel,
                newItem: RVFarmDataModel
            ): Boolean {
                TODO("Not yet implemented")
            }

            override fun areContentsTheSame(
                oldItem: RVFarmDataModel,
                newItem: RVFarmDataModel
            ): Boolean {
                TODO("Not yet implemented")
            }

        }
    }
}

Fragment에서 연결

class HomeFragment : Fragment() {

    private lateinit var homeBinding : FragmentHomeBinding

    private lateinit var adapter: FarmRVAdapter

    private var param1: String? = null
    private var param2: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        adapter = FarmRVAdapter()

//      우리 동네 농장 아이템
        val local_farm_items = mutableListOf<RVFarmDataModel>()
        homeBinding.rvHomeFarm.adapter = adapter
        adapter.submitList(local_farm_items)
        homeBinding.rvHomeFarm.layoutManager = GridLayoutManager(requireActivity(), 2)
        local_farm_items.add(RVFarmDataModel(R.drawable.farm_image_example,"고덕 주말 농장","3평","150,000"))
        local_farm_items.add(RVFarmDataModel(R.drawable.farm_image_example,"고덕 주말 농장","3평","150,000"))
        local_farm_items.add(RVFarmDataModel(R.drawable.farm_image_example,"고덕 주말 농장","3평","150,000"))
        local_farm_items.add(RVFarmDataModel(R.drawable.farm_image_example,"고덕 주말 농장","3평","150,000"))

        return view.root
    }

0개의 댓글