20.07.30 추가내용

MergeAdapter가 ConcatAdapter로 명칭이 변경 되었습니다.


MergeAdapter란?

MergeAdapter는 20년 4월 1일에 배포된 recyclerview:1.2.0-alpha02에 포함된 새로운 클래스로 여러개의 Adapter를 순차적으로 결합시켜 단일 RecyclerView에 표현할 수 있도록 도와준다. 

일반적으로 Adapter를 구현할 때 여러 데이터 소스를 결합한 형태의 자료구조(ex. ArrayList) 를 가지고 있었으나, MergeAdapter는 여러 데이터 소스를 결합하여 하나의 Adapter에서 관리하는 대신 데이터 소스 별로 Adapter를 분리하여 개발자가 좀 더 코드에 집중하고, 이를 재사용하기 쉽도록 캡슐화한다.

Note: 아직 Alpha 스테이지 이므로 Product에 적용하는 것은 추천하지 않습니다.

MergeAdapter를 사용하는 한가지 용례는 현재 데이터 로딩 상태를 헤더나 푸터에 표현하는 것이다. 네트워크나 로컬 데이터베이스로부터 데이터를 가져올 때 일반적으로 프로그레스바를 나타내거나 또는 에러가 발생했을 때 재시도 버튼을 나타낼 수도 있다. 

github, 예제 코드 다운로드

MergeAdapter 설정하기

app/build.gradle 에 다음과 같은 아티팩트 ID를 추가한다

dependencies {
    implementation "androidx.recyclerview:recyclerview:1.2.0-alpha02"
}

MergeAdapter의 구현방법은 간단하다.
만약 HeaderAdapter, PostAdapter, FooterAdapter 처럼 3가지 Adapter가 있다고 가정하면 다음코드와 같이 MergeAdapter를 사용하여 병합할 수 있다.

val firstAdapter: FirstAdapter = …
val secondAdapter: SecondAdapter = …
val thirdAdapter: ThirdAdapter = …
val mergeAdapter = MergeAdapter(firstAdapter, secondAdapter, thirdAdapter)
recyclerView.adapter = mergeAdapter

위와 같이 구현한다면 RecyclerView에서 어댑터 순서로 아이템이 노출된다.

 

MergeAdapter.Config 설정하기

 ViewHolder 공유하기

기본적으로는 Adapter는 Adapter간에 ViewHolder는 공유하지 않고 자신이 생성한 ViewHolder만을 재사용하게 된다. 

만약 Adapter간 ViewHolder를 공유하고 재사용하고 싶다면 MergeAdapter.Config 객체를  MergeAdapter 생성자 인자로 사용하면 된다. 다음 예제코드를 확인하자.

val config = MergeAdapter.Config.Builder()
    .setIsolateViewTypes(false)
    .build()

mergeAdapter = MergeAdapter(config, headerAdapter, postAdapter, footerAdapter)

이제 Adapter들은 ViewHolder를 공유할 수 있게 되었다.

IsloateViewTypes을 설정하게 되면 MergeAdapter 내부에서는 ViewHolder를 관리 하기 위한 스토리지를 다음과 같이 생성하게 된다.

if (config.isolateViewTypes) {
    mViewTypeStorage = new ViewTypeStorage.IsolatedViewTypeStorage();
} else {
    mViewTypeStorage = new ViewTypeStorage.SharedIdRangeViewTypeStorage();
}

Stable Id 사용하기

notifyDataSetChanged 메서드와 함께 stable id를 사용하는 대신, 데이터 세트의 변경 사항에 대해 RecyclerView에 추가 정보를 제공하는 Adapter의 특정 알림 이벤트를 사용하는 것을 추천한다. 이렇게 하면 RecyclerView의 보다 효율적으로 갱신하면서도 더 나은 애니메이션 효과를 볼 수 있다. 만약 ListAdapter를 사용하는 경우에는 알림 이벤트가 DiffUtil 콜백으로 처리된다.

stable id를 사용하지 않는 경우에는 MergeAdapter.Config를 사용하여 다음과 같이 stable id 전략을 변경할 수 있다.

val config = MergeAdapter.Config.Builder() 
    .setStableIdMode(MergeAdapter.Config.StableIdMode.NO_STABLE_IDS) 
    .build()

ISOLATED_STABLE_IDS 

MergeAdapter에 속해 있는 모든 Adapter들이 stable id를 반드시 가지고 있어야 한다. 두개의 서로 다른 어댑터는 서로를 알지 못하기 때문에 동일한 stable id를 반환 할 수 있기 때문에 MergeAdapter는 각 Adapter의 ID풀을 서로 분리하여 RecyclerView에게 전달하기 전에 stable id를 덮어 쓴다. 그렇기 때문에 ViewHolder.getItemId()에서 반환된 값과 Adapter.getItemId 에서 반환된 값이 서로 다를 수 있다. 만약 stable id가 없는 어댑터를 이 모드에서 사용하게 되면 IllegalArgumentException이 발생한다.

NO_STABLE_IDS

MergeAdapter.Config의 기본 값이며, 이 모드로 설정한 경우 MergeAdapter는 하위 Adapter가 가지고 있는 stable id를 무시한다. 

SHARED_STABLE_IDS

MergeAdapter에 속해 있는 모든 Adapter들이 stable id를 반드시 가지고 있어야 한다. ISOLATED_STABLE_IDS와 달리 MergeAdapter는 반환된 아이템의 ID를 재정의 하지 않는다. 이 모드에서는 Adapters들이 서로를 인식하게 되고, Adapter간에 아이템이 이동하지 않는한 동일한 ID를 반환하지 않아야 한다. stable id가없는 어댑터를 추가하면 IllegalArgumentException이 발생한다.

그 외 다른 특징들

데이터의 변경 알림 방식

MergeAdapter에 속해 있는 Adapter 하나가 데이터 변경 알림 메서드를 호출하면 MergeAdapter는 RecyclerView를 갱신하기 전에 새로운 아이템의 위치를 계산하게 된다.

RecyclerView의 관점에서 notifyItemRangeChanged는 아이템이 동일하고 내용만 변경되었음을 의미한다. notifyDataSetChanged 메서드는 변경 이전과 변경 이후의 관계가 없으므로  notifyDataSetChanged를 notifyItemRangeChanged로 매핑할 수 없다.

개별적인 Adapter가 notifyDataSetChanged를 호출하면 MergeAdapter는 notifyItemRangeChanged가 아닌 notifyDataSetChanged을 호출한다.

MergeAdapter를 사용한다면 notifyDataSetChanged()를 직접 호출하기 보다는 ListAdapter 또는 SortedList와 같이 자동으로 갱신을 수행하는 Adapter 구현을 사용하기를 추천한다.

ViewHolder 포지션 찾기

이전에 ViewHolder.getAdapterPosition()을 사용하여 어댑터에서 ViewHolder의 포지션을 알 수 있었지만. 이제는 여러 어댑터를 병합하므로 ViewHolder.getBindingAdapterPosition() 메서드를 사용하여 포지션을 찾아야한다.

ViewHolder를 공유하는 경우, ViewHolder를 마지막으로 바인딩 한 어댑터를 얻으려면  ViewHolder.getBindingAdapter()를 사용하자.

마무리

다양한 뷰 또는 뷰홀더를 사용하여 RecyclerView를 구현하는 경우가 많다. 여러가지 데이터 타입을 하나의 리스트로 관리하는게 일반적이다 보니 관심사 분리가 쉽지 않았는데, MergeAdapter를 사용하면 각 어댑터와 데이터 소스를 캡슐화하는 것이 수월해진다. 특정 어댑터의 데이터목록만 갱신하게 되는 경우 유지 보수 측면이나 퍼포먼스 측면에서도 많은 이득을 얻을 것으로 기대된다. (MergeAdapter가 Stable 버전으로 빨리 출시되길….)

Buy me a coffeeBuy me a coffee
카테고리: Android

0개의 댓글

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.