DiffUtil

AsyncListDiffer / ListAdapter

DiffUtil

DiffUtil


RecyclerView 의 데이터가 변경되면 RecyclerView.Adapter 가 제공하는 notify···() 함수를 통해 ViewHolder 내용을 갱신할 수 있지만, 갱신이 필요 없는 ViewHolder 를 같이 갱신하는 등의 불필요한 리소스 낭비가 발생한다.

DiffUtil 은 2개의 데이터 셋을 받아 비교하여 변경된 부분만을 파악해서 RecyclerView 에 반영한다.

  • DiffUtil.Callback() 을 상속 받아 사용하며 백그라운드에서 동작한다.
  • areItemsTheSame() 은 두 객체가 동일한지 비교한다. (===)
  • areContentsTheSame() 은 객체의 필드가 같은지 확인한다. (==)


AsyncListDiffer


DiffUtil 을 사용하기 위한 클래스이다.

DiffUtil.Callback() 을 전달 받은 AsyncListDiffer 객체를 만들면 currentList() 로 데이터를 참조하고, submitList() 로 데이터를 갱신할 수 있다.

class DataAdapter : RecyclerView.Adapter<DataViewHolder>() {
		
    private val differ: AsyncListDiffer<Data> = AsyncListDiffer(
        this, object: ItemCallback<Data>() {
            fun areItemsTheSame(
                oldItem: Data, newItem: Data
            ): Boolean = oldItem.id == newItem.id

            fun areContentsTheSame(
                oldItem: Data, newItem: Data
            ): Boolean = oldItem == newItem
        }
	)
    
    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): DataViewHolder {
        return DataViewHolder(
            LayoutInflater.from(parent.context).inflate(
                R.layout.items_data,
                parent,
                false
            )
        )
    }

    override fun onBindViewHolder(
        holder: DataViewHolder,
        position: Int
    ) {
        get(position)?.let {
            holder.bind(it)
        }
    }

    override fun getItemCount(): Int = differ.currentList.size


    fun addData(list: List<Data>) = differ.submitList(list)
}



ListAdapter


AsyncListDiffer 를 사용하기 쉽게 Wrapping 한 클래스이다.

class DataAdapter : ListAdapter.Adapter<Data, DataViewHolder>(
    object: ItemCallback<Data>() {
        fun areItemsTheSame(
            oldItem: Data, newItem: Data
        ): Boolean = oldItem.id == newItem.id

        fun areContentsTheSame(
            oldItem: Data, newItem: Data
        ): Boolean = oldItem == newItem
    }
) {	    
    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): DataViewHolder {
        return DataViewHolder(
            LayoutInflater.from(parent.context).inflate(
                R.layout.items_data,
                parent,
                false
            )
        )
    }		

    override fun onBindViewHolder(
        holder: DataViewHolder,
        position: Int
    ) {
        get(position)?.let {
            holder.bind(it)
        }
    }
}
essential