안녕하세요. 점냥입니다:)
굉장히 오랜만에 글을 쓰네요 ㅎ. 다들 잘 지내셨나요?
오늘은 RecyclerView 새로운 버전이 나오면서 새로운 Adapter인 ConcatAdapter
에 대해서 알아보겠습니다.
사실 처음에는 MergeAdapter라는 이름으로 나왔지만 ConcatAdpater로 이름이 변경되었습니다.
ConcatAdapter
RecyclerView는 하나의 Adapter만을 가지며, 반복되는 뷰 리스트를 그리는 위젯입니다.
그 이유 때문에 사진과 같이 Header 혹은 Footer 등을 표현하기 위해 포지션으로 구분하고, 각 뷰 타입의 로직을 포지션 별로 구분하여 넣어주어야 했습니다. 이는 한 클래스에서 2가지 역할을 가지는 방법이며, 뷰가 복잡해질수록 코드가 비대해지는 안 좋은 방법이었죠. 이러한 단점을 해결하기 위해 ConcatAdapter가 새로 나왔습니다.
val firstAdapter: FirstAdapter = …
val secondAdapter: SecondAdapter = …
val thirdAdapter: ThirdAdapter = …
val concatAdapter = ConcatAdapter(firstAdapter, secondAdapter,
thirdAdapter)
recyclerView.adapter = concatAdapter
ConcatAdapter는 RecyclerVIew가 Multiple Adapter를 가지도록 도와주는 Adapter이며 Adapter의 순서대로 뷰가 그려집니다.
이게 무슨 장점이냐 하면
Header Footer 각 뷰 타입에 맞는 로직을 Adapter로 분리가 되는 것으로, 객체지향 원칙에 맞게 한 클래스가 하나의 역할을 가지게 만들 수 있습니다!
Dependency 추가
implementation "androidx.recyclerview:recyclerview:1.2.0-rc01"
Android 개발자 사이트 RecyclerView 문서를 읽어보면 라이브러리 버전 내용을 볼 수 있습니다.
현재 안정된 버전은 1.1.0이지만 ConcatAdapter를 사용하기 위해서는 1.2.0 버전을 사용해야 합니다.
한글 문서가 아닌 영어 문서로 읽어야 1.2.0-rc01 버전이 다음 릴리즈에 등록된 것을 확인할 수 있으며 Next Release에 등록되어 있는 만큼 1.2.0-rc01 버전은 안정되고 버그가 없다는 뜻입니다. 알파, 베타, rc 버전에 대한 자세한 내용은 링크에서 확인할 수 있습니다.
ConcatAdapter 사용하기
기본적으로 ConcatAdapter를 사용하는 것은 굉장히 쉽습니다. 제가 작성한 일부분의 코드를 보여 드리겠습니다!
AddAdapter.class
class AddAdapter() : RecyclerView.Adapter<AddViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddViewHolder {
return AddViewHolder(
ItemImageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
}
override fun onBindViewHolder(holder: AddViewHolder, position: Int) {}
override fun getItemCount(): Int = 1
}
ImageAdapter.class
class ImageAdapter() : ListAdapter<ReviewImage, ImageHolder>(ImageDiffUtil) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageHolder {
return ImageHolder(
ItemImageBinding.inflate(
LayoutInflater.from(parent.context),
parent, false
)
)
}
override fun onBindViewHolder(holder: ImageHolder, position: Int) {
holder.bind(getItem(position))
}
}
Activity.class
binding.createImageRecyclerView.apply {
layoutManager = GridLayoutManager(context, IMAGE_SPAN_COUNT)
val addAdapter = AddAdapter()
val imageAdapter = ImageAdapter()
adapter = ConcatAdapter(addAdapter, imageAdapter)
}
DataBinding을 사용하여 매우 간단하게 Adapter가 사용되고 있습니다. 따라서 "굳이 분리되지 않아도 복잡하지 않겠네요"라는 생각이 드실 수 있지만 더 좋은 코드, 깨끗한 코드를 위해 관심사 분리, 의존성 줄이기 등은 꼭 필요합니다.
ConcatAdapter의 특성
ConcatAdapter의 Adapter들은 각자의 ViewHolder Pool을 가지고 있어 서로의 ViewHolder를 재사용할 수 없습니다.
하지만 만약 모든 Adapter가 동일한 ViewHolder를 사용하고 있다면 ViewHolder Pool을 공유하게 하는 것이 더 효율적인 방법이 되겠죠.
val addAdapter = AddAdapter(viewModel)
val imageAdapter = ImageAdapter(viewModel)
val config = ConcatAdapter.Config.Builder()
.setIsolateViewTypes(false)
.build()
adapter = ConcatAdapter(config, addAdapter, imageAdapter)
관련있는 RecyclerView 1.2.0에서의 변경점
일반적인 Adapter에서 클릭 리스너에 포지션을 전달할 때 ViewHolder.getAdapterPosition 함수를 사용했습니다.
하지만 위 함수는 RecyclerVIew 1.2.0 라이브러리에서 Deprecated 되어 버리고 말았습니다. 대신 ViewHolder.getBindingAdapterPosition()
,
ViewHolder.getAbsoluteAdapterPosition
이라는 함수가 생겼습니다.
getBingAdapterPosition은 Deprecated된 함수와 비슷하게 Adapter내에 ViewHolder의 위치를 반환하고, getAbsoluteAdapterPosition 함수는 ViewHolder를 가진 Adapter 내의 위치가 아닌 RecyclerView 에서 바라보는 절대적인 위치를 반환합니다. 따라서 ConcatAdapter와 같은 Multiple Adapter 인 경우 주의해야합니다.
'Android > Common' 카테고리의 다른 글
[Android] 좋아요 기능으로 알아보는 더블 클릭 방지하는 방법 (0) | 2021.09.04 |
---|---|
[Android] fromHtml Bullet 속성 변경하기 (0) | 2021.07.03 |
[Android] LiveData의 Data를 한번만 관찰 (0) | 2021.03.15 |
[Android] MVVM 적용하기 - View와 ViewModel (2) | 2021.03.10 |
[Android] 상태바 투명으로 만드는 여러 방법에 대한 일지 (0) | 2021.03.08 |