728x90


 

ListAdapter와 DiffUtil에 대해 알아보기 전에 우선 RecyclerView와 LiveData에 대해 알아봐야 한다.

 

따라서 전에 작성한 포스팅을 첨부한다.

https://jminie.tistory.com/144?category=1040997 

 

안드로이드 [Kotlin] - RecyclerView로 랜덤한 색상으로 사각형 채워보기

📌 RecyclerView 란? https://developer.android.com/guide/topics/ui/layout/recyclerview RecyclerView로 동적 목록 만들기  | Android 개발자  | Android Developers RecyclerView로 동적 목록 만들기   A..

jminie.tistory.com

https://jminie.tistory.com/145

 

안드로이드 [Kotlin] - LiveData로 계산기 만들기

📌 LiveData란? class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { ... DB에서 초기 아이템 목록 호출 UI 업데이트 추가 버튼 클릭 리스너 { 아이템 추가 UI 업..

jminie.tistory.com

 

 


 

 

📌  DiffUtil 이란?

기존 RecyclerView 의 Adapter 가 하는 일은 

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

이때 기존에는 notifyDataSetChanged()를 사용해왔지만 이는 데이터의 변경을 감지하고 리스트를 모두 바꾸어 버리므로 시간 지연이 발생할 가능성이 높다.

 

이에 반해 RecyclerView.DiffUtil은

  • 현재 데이터 리스트와 교체될 데이터 리스트를 비교하고
  • 진짜 바뀌어야 할 데이터만 바꿔줌으로써
  • 훨씬 빠른 시간 내에 효율적으로 데이터 교환을 할 수 있게 한다.

 

 

 

📌  ListAdapter 구현하기

DiffUtil 확장 클래스 중에는 AsyncListDiffer와 같은 비동기 처리를 지원해주는 클래스가 있다. AsyncListDiffer를 내부적으로 사용하고 있는 클래스가 바로 ListAdapter 클래스이다. 우리는 이제 그저 ListAdapter를 상속한 클래스를 만든 것만으로도 AsyncListDiffer를 사용할 수 있게 된다.

 

필자는 코드스쿼드 미션 중 ListAdapter 를 사용한 코드를 예시로 들어보려고 한다.

 

 

아이템을 담을 DataClass 작성

package com.example.photos.step3.model

import android.graphics.Bitmap

data class DoodleImage(val id: Int, val bitmap: Bitmap)

아이템을 분류하기 위한 id 값과 이미지를 담을 bitmap 값으로 DataClass를 작성한다.

 

 

 

DiffUitlCallback 클래스 정의

DiffUtil을 사용하기 위해서 DiffUtil.Callback이라는 기능을 구현해야 한다. 이때 다음과 같은 사항들을 구현하도록 한다.

  • areItemsTheSame : 기존 어댑터와 새롭게 변경되는 어댑터의 아이템이 같은지 확인한다. 각 아이템의 고유 ID값을 활용하여 비교한다. 리턴 타입은 Boolean이다.
  • areContentsTheSame : 기존 어댑터와 변경되는 어댑터의 아이템 안의 내용을 비교한다. areItemsTheSame 에서 true가 나올 경우 추가적으로 비교하기 위해서 사용하는 함수이다.

 

만약 여기서 areItemsTheSame를 잘못 정의하시면 모든 Item을 다 지우고 새 아이템을 Insert 하게 되므로 위에 설명했던 기존의 notifyDataSetChanged()와 달라질 것이 없게 되므로 주의한다.

 

 

Callback 클래스를 object 키워드를 사용하여 싱글턴으로 만들어 준다.

object MyDoodleDiffCallback : DiffUtil.ItemCallback<DoodleImage>() {
    override fun areItemsTheSame(oldItem: DoodleImage, newItem: DoodleImage): Boolean {
        return oldItem.id == newItem.id
    }

    override fun areContentsTheSame(oldItem: DoodleImage, newItem: DoodleImage): Boolean {
        return oldItem == newItem
    }
}

 

 

ListAdapter 구현 및 부착

ListAdapter를 정의할 때는 다음과 같은 형태를 지닌다.

ListAdapter<데이터 클래스, 리사이클러뷰 뷰홀더>(개발자 정의 콜백)

 

ListAdapter는 데이터 클래스를 받고 있다는 게 특징인데, 이는 사용자가 어댑터 내에서 데이터 리스트를 정의하지 않고 리스트 자체에서 데이터 리스트를 정의하기 때문이다.

 

따라서 ListAdapter에서 구현해야 할 함수를 보면 getItemCount가 사라져 있다.

 

 

 

ListAdapter에서 사용할 수 있는 주요 Method

  • getItem(position: Int) : protected method이기 때문에 클래스 내부에서 구현할 때 사용한다. 어댑터 내 List Indexing을 할 때 활용할 수 있다.
  • getCurrentList() : 어댑터가 가지고 있는 리스트를 가져올 때 사용한다.
  • submitList(MutableList <T> list) : 리스트 항목을 변경하고 싶을 때 사용한다.

 

 

 

아래와 같이 ListAdapter를 구현해준다.

class DoodleAdapter :
    ListAdapter<DoodleImage, DoodleAdapter.DoodleViewHolder>(MyDoodleDiffCallback) {

    class DoodleViewHolder(binding: DoodleSquareBinding) : RecyclerView.ViewHolder(binding.root) {
        private val imageView: ImageView = binding.doodleImageView

        fun bind(bitmap: Bitmap) {
            imageView.setImageBitmap(null)
            imageView.scaleType = ImageView.ScaleType.CENTER_CROP
            imageView.setImageBitmap(bitmap)
        }
    }

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): DoodleViewHolder {
        val binding = DoodleSquareBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return DoodleViewHolder(binding)
    }

    override fun onBindViewHolder(holder: DoodleViewHolder, position: Int) {
        val item = getItem(position)
        holder.bind(item.bitmap)
    }
}

 

 뷰바인딩을 이용하기 위해 ViewHolder 부분의 인자에 DoodleSquareBinding 타입의 binding을 인자로 넣어두고 상속받는 ViewHolder의 인자에 binding.root를 넣어준다.

 

 

또한 fun bind 부분은 뷰 홀더 부분의 View와 데이터를 연결시켜 주는 함수를 구현한 것이다.

 

 

 

 

 

 

 

 

 

 

 

 

Reference:

https://huiung.tistory.com/183

https://s2choco.tistory.com/33

https://lakue.tistory.com/17

 

복사했습니다!