RecyclerView.DiffUtil

RecyclerView의 성능 향상을 위해 사용하는 DiffUtil 은 서로 다른 아이템인지를 체크하여 달라진 아이템만 갱신을 도와주는 Util이다.

현재 데이터 리스트와 교체될 데이터 리스트를 비교하고, 수정되어야하는 데이터만 바꿔줌으로써 빠른시간내에 데이터 교환을 할 수 있다.

DiffUtil의 필수 상속 함수

ListAdapter

DiffUtil을 활용하여 리스트를 업데이트할 수 있는 기능을 추가한 Adapter이다.

기존 어댑터와 비교해서 추가로 DiffiUtil 기능에 대한 콜백 기능만 구현하면 되므로 생산성, 효율성을 높일 수 있다.

ListAdapter<데이터 클래스, 뷰홀더>(DIff 콜백)

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

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

// DiffCallback
class MainDiffCallback : DiffUtil.ItemCallback<UserRepositories>() {
    override fun areItemsTheSame(oldItem: UserRepositories, newItem: UserRepositories): Boolean {
        // 현재 리스트의 노출하고 있는 아이템과, 새로운 아이템이 같은지 비교(고유값으로 비교)
        // 여기선 레포지토리의 url을 비교해준다.
        return oldItem.html_url == newItem.html_url
    }

    override fun areContentsTheSame(oldItem: UserRepositories, newItem: UserRepositories): Boolean {
        // 현재 리스트에 노출하고 있는 아이템과, 새로운 아이템의 equals 비교
        // areItemsTheSame에서 true가 나올경우 추가적으로 비교
        return oldItem == newItem
    }
}

예제는 GitHub API를 가져와 유저의 ID를 검색하면 레포지토리를 출력하는 예제.

// ListAdapter
class MainAdapter : ListAdapter<UserRepositories, MainAdapter.MainViewHolder>(MainDiffCallback()){
		override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.repositories_item, parent, false)
        val binding = RepositoriesItemBinding.bind(view)
        return MainViewHolder(binding)
    }

    override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
        holder.bind(getItem(position))
    }
}
// MainActivity 뷰와 데이터 매핑 부분
private fun setData(avatar_url: String, login: String, url: String, repositories: ArrayList<UserRepositories>) {
    Glide.with(this).load(avatar_url).into(binding.profileImageView)
    binding.profileUsernameTextView.text = login
    binding.projectGitUrlTextView.text = url

    val mainAdapter = MainAdapter().apply {
        submitList(repositories)
        setOnItemClickListener(object: MainListener{
            override fun onClick(view: View, position: Int) {
                val intent = Intent(Intent.ACTION_VIEW, repositories[position].html_url.toUri())
                startActivity(intent)
            }
        })
    }
    binding.repoReclerView.adapter = mainAdapter
}