Android - RecyclerView에서의 ListAdapter 활용

2024. 7. 18. 11:09[개발]/Kotlin 활용 앱 개발

# RecyclerView.Adapter의 단점

- item Delete할 때 notifyItemRemoved(position)을 반복하다 보면 오류가 자주 발생함

  (내가 position 1에 있는 아이템을 삭제하기 직전에 다른 사람이 같은 아이템을 삭제해버려서 내가 position 2에 있던 아이템을 삭제하게 되는 등)

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 0(offset:0).state:4
        at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3382)

- position 명시가 헷갈려서 notifyDataSetChanged() 를 자주 호출하면 경고가 계속 발생함(데이터 통째로 업데이트해서 비권장)

- RecyclerView.Adapter는 정적인(변화가 없는) Data list 구현에는 무척 최적화 되어 있지만, data가 계속 insert/delet가 반복된다면 position 관리와 코드 가독성, 휴먼 에러 (notify 누락, position 오류 등)에 취약

- 자동으로 알아서 notify해주는 기능이 필요함

 

# ListAdapter

- 데이터 목록이 업데이트될 때마다 전체 데이터를 업데이트하는 대신 변경된 부분만 갱신시켜줌

- 내장된 DiffUtil을 통한 자동 데이터 변경 감지 및 UI 갱신 수행 

- 커스터마이징이 상대적으로 제한적 / 초보자에게 다소 복잡

abstract class ListAdapter<T, VH : RecyclerView.ViewHolder?> : RecyclerView.Adapter
class VideoListAdapter : ListAdapter<Video, VideoListAdapter.VideoViewHolder>(object : DiffUtil.ItemCallback<Video>() {
    override fun areItemsTheSame(oldItem: Video, newItem: Video): Boolean {
        // 비디오 id가 같은지 확인
        return oldItem.id == newItem.id
    }

    override fun areContentsTheSame(oldItem: Video, newItem: Video): Boolean {
        // 모든 필드가 같은지 확인 (data class의 equals 사용)
        return oldItem == newItem
    }
}) {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideoViewHolder {
        // ViewHolder 생성 로직
        // 예시: return VideoViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.video_view_holder, parent, false))
    }

    override fun onBindViewHolder(holder: VideoViewHolder, position: Int) {
        // ViewHolder 바인딩 로직
        holder.bind(getItem(position))
    }

    inner class VideoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        // ViewHolder 구현
        // 예시: fun bind(video: Video) { /* 아이템 바인딩 로직 */ }
    }
}

- RecyclerView.Adapter를 상속받아서 구현

- areItemsTheSame(), areContentsTheSame() 함수를 오버라이딩해서 직접 구현해줘야 함 (DiffUtil)

-  RecyclerView.Adapter와 다르게 itemList를 받아오는 게 아니라 itemList를 내장하고 있음

  =>  item을 받아오려면 getItem(position)으로 접근

- itemList를 내장시키고 한 번에 쓰기 위해 MainActivity에서 최초로 한 번 리스트를 넣어줘야 함

// submitList() 활용
// Ex)
adapter = VideoAdapter.apply { submitList(VideoList.list.toList()) }

 

# DiffUtil

(1) areItemsTheSame()

- 1차 면접의 개념

- Primary key(고유 정보)를 비교해서 같은 정보를 비교 대상으로 선정

- 같으면 다음 단계로 진행

 

(2) areContentsTheSame()

- 2차 면접의 개념

- 실제 내용이 바뀌었는지 확인