데이터를 받아오고 이를 레이아웃에 직접 연결하는 함수를 실행시키는 클래스
= ListView나 App의 특정 데이터와 RecyclerView의 view를 Bind하는 것을 도와준다
RecyclerViewAdapter 같은 경우, 전체 리스트를 통째로 업데이트해야 할때 100개의 아이템들이 있다면 100개가 모두 업데이트된다.
그런데 안바뀌는 항목이 더 많다면..? 비효율적이다
-> 그래서 DiffUtil 이 있다
기존 리스트와 업데이트 된 리스트의 차이를 계산하고 실제로 변환할 리스트 아이템들의 결과를 반환하는 유틸리티 클래스
주로 RecyclerView Adpater의 업데이트를 계산하는데 사용되고 ListAdapter에서 DiffUtil을 활용해서 차이점을 계산한다.
DiffUtil.Callback 이라는 기능을 구현해야 한다!
//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
}
}
간단히 말해서, DiffUtil을 활용해서 리스트를 업데이트 할 수 있는 기능을 추가한 Adapter
AsyncListDiffer 클래스를 쉽게 사용하기 위해 ListAdapter를 활용할 수 있다. ListAdapter의 생성자에서 DiffUtil.ItemCallback을 인자로 넘겨주면서 사용한다.List 데이터를 표현해주며 List를 백그라운드 스레드에서 diff(차이)를 처리하는 특징이 있다.
class BookAdapter:ListAdapter<Book,BookAdapter.BookItemViewHolder>(diffUtil) {
//ListAdapter<데이터클래스,리사이클러뷰 뷰홀더> 를 인자로 받는다-
inner class BookItemViewHolder(private val binding: ItemBookBinding):RecyclerView.ViewHolder(binding.root){
//뷰홀더: 내가 넣고자하는 data를 실제 레이아웃의 데이터로 연결시키는 기능
fun bind(bookModel:Book){//view와 데이터를 연결시키는 함수-/>뷰에 데이터 넣음
binding.titleTextview.text=bookModel.title
binding.descriptionTextView.text=bookModel.description
Glide
.with(binding.coverImageView.context) //context가 어댑터에 없다 -> 뷰에 있겠죠?
.load(bookModel.coverSmallUrl)
.into(binding.coverImageView)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookItemViewHolder {
//미리 만들어진 뷰홀더가 없는 경우 새로 생성하는 함수(레이아웃 생성)
return BookItemViewHolder(ItemBookBinding.inflate(LayoutInflater.from(parent.context),parent,false))
}
override fun onBindViewHolder(holder: BookItemViewHolder, position: Int) {
//실제로 뷰홀더가 뷰에 그려졌을때 데이터를 뿌려주는 바인드해주는 함수(뷰홀더가 재활용될때 실행)
holder.bind(currentList[position])
}
//diffutil사용하려면 diffutil.callback이라는 기능을 구현해야함
companion object{
val diffUtil=object:DiffUtil.ItemCallback<Book>(){
override fun areItemsTheSame(oldItem: Book, newItem: Book): Boolean {
return oldItem.id==newItem.id
}
override fun areContentsTheSame(oldItem: Book, newItem: Book): Boolean {
return oldItem==newItem
}
}
}
}
#MainActivity.kt에 추가
class MainActivity : AppCompatActivity() {
private lateinit var binding:ActivityMainBinding
private lateinit var adapter:BookAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_main)
binding=ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
adapter= BookAdapter()
initBookRecyclerView()
val retrofit= Retrofit.Builder()
.baseUrl("https://book.interpark.com")
.addConverterFactory(GsonConverterFactory.create()) // Json데이터를 사용자가 정의한 Java 객채로 변환해주는 라이브러리
.build() //레트로핏 구현체 완성!
val bookService=retrofit.create(BookService::class.java)
bookService.getBestSeller("38845BE9BD0EBEDF271A2D5BC770C5BEEBB2D38910F504545CE384C6692DA6D4")
.enqueue(object: Callback<BestSellerDto>{
override fun onFailure(call: Call<BestSellerDto>, t: Throwable) {
//todo 실패처리
Log.d(TAG,t.toString())
}
override fun onResponse(call: Call<BestSellerDto>, response: Response<BestSellerDto>) {
//todo 성공처리
if(response.isSuccessful.not()){
return
}
response.body()?.let{
//body가 있다면 그안에는 bestSellerDto가 들어있을것
Log.d(TAG,it.toString())
it.books.forEach{ book->
Log.d(TAG,book.toString())
}
adapter.submitList(it.books)
//ListAdapter는 내부적으로 AsyncListDiffer를 사용하면서, RecyclerView의 adapter처럼 이용이 가능합니다.
// 따라서 우리는 최종적으로 ListAdapter를 상속하는 adapter 클래스를 만들고, ListAdapter의 파라미터에 diffutil의 callback을 구현해서 넘겨주면
// 내부에서 submitlist( 바뀔 데이터 ) 라는 하나의 메서드로 모든 작업을 처리 할 수 있습니다!!!!
}
}
})
}
https://velog.io/@l2hyunwoo/Android-RecyclerView-DiffUtil-ListAdapter
-> 사용 이유 / 설명과 예제
https://youngest-programming.tistory.com/474
-> RecyclerView->ListAdapter+DiffUtil 예제정리