RecyclerView의 성능 향상을 위해 사용하는 DiffUtil
은 서로 다른 아이템인지를 체크하여 달라진 아이템만 갱신을 도와주는 Util이다.
현재 데이터 리스트와 교체될 데이터 리스트를 비교하고, 수정되어야하는 데이터만 바꿔줌으로써 빠른시간내에 데이터 교환을 할 수 있다.
areItemsTheSame
: 현재 리스트에 노출하고 있는 아이템과 새로운 아이템이 서로 같은지 비교한다. 보통 고유한 ID 값을 체크(두 아이템이 같은 객체인지 여부를 반환)areContentsTheSame
: 현재 리스트에 노출하고 있는 아이템과 새로운 아이템의 equals를 비교한다. areItemsTheSame
이 true를 반환할 때만 호출된다.DiffUtil을 활용하여 리스트를 업데이트할 수 있는 기능을 추가한 Adapter이다.
기존 어댑터와 비교해서 추가로 DiffiUtil 기능에 대한 콜백 기능만 구현하면 되므로 생산성, 효율성을 높일 수 있다.
ListAdapter<데이터 클래스, 뷰홀더>(DIff 콜백)
ListAdapter는 데이터 클래스를 받고 있다는게 특징이다. 이는 사용자가 어댑터 내에서 데이터 리스트를 정의하지 않고 리스트 자체에서 정의하기 때문. 그래서 기본 RecyclerView에 있는 getItemCount
가 없다.
getItem()
: 어댑터 내부에서 List를 Indexing할 때 사용getCurrentList()
: 어댑터를 가지고 있는 리스트를 반환한다.submitList()
: 리스트의 항목을 어댑터에 Set해준다. 만약 리스트가 있다면 DiffCallback이 호출된다.// 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
}