안드로이드로 앱을 개발하다 보면 여러 어댑터를 만나게 됩니다. 어댑터(영어: adapter, adaptor)는 다른 전기나 기계 장치를 서로 연결해서 작동할 수 있도록 만들어 주는 결합 도구를 뜻합니다. 안드로이드에서 이러한 어댑터들은 리스트뷰나 ViewPager같이 연속된 객체들을 보여줄 때 어떻게 연결한 것인지에 대해서 설정할 수 있습니다. 오늘은 리사이클러뷰의 어댑터들에 대에서 포스팅을 해볼까 합니다.
리사이클러뷰(RecyclerView)는 여러 객체들을 아이템 단위로 구성하여 화면에 출력하는 뷰그룹이며, 한 화면에 표시되기 힘든 많은 수의 데이터를 스크롤 가능한 리스로 표시해 주는 위젯입니다. 그렇다면 이게 리스트 뷰랑 다른 점이 뭐가 다르냐고 말할 수 있습니다. 리사이클러뷰는 이 리스트뷰의 상위 호완 격인 위젯입니다. 리스트 뷰의 단점은 항목인 갱신될 때마다 아이템뷰를 매번 아이템뷰를 새로 구성해야 합니다. 그렇지만 리사이클러뷰에서는 뷰홀더 패턴을 만들어 사용하지 않는 뷰들을 재활용하여 보여줍니다.
이러한 리사이클러뷰는 어댑터가 필수적입니다. 어댑터에서 어떠한 데이터를 보여줄 건지 설정해줘야 하기 때문입니다
리사이클러뷰의 어댑터는 일반적으로 RecyclerView.Adapter 와 ListAdapter로 2개로 나눌 수 있습니다.
RecyclerView.Adapter 같은 경우
class RecyclerviewAdapter(private val itemArr: Array<String>) :
RecyclerView.Adapter<RecyclerviewAdapter.ViewHolder>() {
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(str: String) {
itemView.findViewById<TextView>(R.id.item_textview).text = str
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val context: Context = parent.context
val inflater: LayoutInflater =
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val view = inflater.inflate(R.layout.item_recyclerview, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return itemArr.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val text: String = itemArr[position]
holder.bind(text)
}
}
해당코드는 리사이클러뷰어댑터의 예시 코드입니다. 해당코드에서 보는 것과 같이 RecyclerView.Adapter 같은 경우 RecyclerView.ViewHolder를 상속받은 뷰홀더를 제네릭으로 받기 때문에 따로 ViewHolder를 만들어 줘야 합니다.
ViewHolder의 경우 어떤 뷰를 쓰는지 매개변수로 받아 RecyclerView.ViewHolder의 매개변수로 집어넣어 줘야 합니다.
이후 RecyclerView.Adapter는 필수적으로 3가지 함수를 구현해줘야 합니다. 어댑터 생성 시 화면에 맞게 ViewHolder를 채워주는 onCreateViewHolder(), 아이템의 총크기를 나타내는 getItemCount(), 순서에 맞게 ViewHolder에 값들을 넣어주는 onBindViewHolder가 있습니다. 이러한 함수들만 지정해 준다면 RecyclerView.Adapter를 구현해 줄 수 있습니다. 그렇다면 이렇게 리사이클러뷰에서 지원하는 어댑터가 있음에도 ListAdapter가 있는 이유는 무엇일까요?
그것은 ListAdapter의 DiffUtil 때문입니다. ListAdapter는 전반적으로 RecyclerView.Adapter와 비슷하지만 DiffUtil이라는 것을 가지고 있습니다. 이 DiffUtil은 데이터 변경 사항을 계산하고 적용하는 작업을 자동으로 처리합니다. 이는 성능 개선과 개발자의 부담을 줄여줍니다. 기존은 RecyclerView.Adapter는 데이터 변경 시 notifyItemChanged같이 함수를 불러와 수동으로 갱신을 해줘야 했습니다. 또한 갱신 시 같은 아이템이더라도 한 번 더 바인드 하기 때문에 필요이상의 성능 낭비가 생깁니다.
class RecyclerViewListAdapter :
androidx.recyclerview.widget.ListAdapter<String,RecyclerViewListAdapter.ViewHolder>(diffUtil) {
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(str: String) {
itemView.findViewById<TextView>(R.id.item_textview).text = str
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val context: Context = parent.context
val inflater: LayoutInflater =
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val view = inflater.inflate(R.layout.item_recyclerview, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
}
companion object{
val diffUtil = object : DiffUtil.ItemCallback<String>() {
override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
return oldItem == newItem
}
}
}
}
해당코드는 ListAdapter의 예시 코드입니다.
데이터들은 ListAdapter의 생성자로 준 diffUtil로 만들어진 mDiffer에 담겨있습니다. diffUtil 같은 경우 content 즉 내용이 같은 경우와 주소가 같은 경우로 나누어 아이템 갱신 시 새로 변경해야 하는 부분만을 찾아냅니다.
이처럼 diffUtill 덕분에 데이터 변경 감지와 업데이트 관련 작업을 더 효율적으로 처리하기 위해 권장되는 클래스입니다. 데이터 변경 시의 성능 향상과 코드 간결성을 위해 ListAdapter를 사용하는 것이 좋습니다.
참고
안드로이드 리사이클러뷰 기본 사용법. (Android RecyclerView) : https://recipes4dev.tistory.com/154
'안드로이드 > 안드로이드' 카테고리의 다른 글
[Android/Kotlin] 알림(Notification)아이디 채널아이디 (2) | 2023.08.30 |
---|---|
[Android/Kotlin] 뷰 바인딩(ViewBinding) (2) | 2023.08.28 |
[Android/Kotlin] ActivityResultLauncher는 무었일까요? (0) | 2023.08.07 |
[안드로이드] context는 무었인가 (0) | 2022.04.30 |
[안드로이드]Toast 메시지 (0) | 2022.03.22 |