Recyclerview는 많은 데이터를 제한된 영역 내에서 유연하게 스크롤 가능한 리스트로 표시해 주는 위젯입니다.
Recyclerview는
뷰를 재활용하므로 앱 성능과 메모리 관리 측면에서도 매우 유용하며, 대규모 데이터를 효율적으로 쉽게 표시할 수 있습니다. 안드로이드 UI 툴킷의 중요한 구성 요소이며 많은 안드로이드 앱에서 널리 사용되고 있습니다.
Recyclerview를 사용할 때의 이점 중 하나는 커스텀이 매우 용이하다는 것입니다. 이 포스팅에서는 사용자의 특정 요구에 맞게 커스텀 하는 몇 가지 방법을 살펴보겠습니다.
Custom LayoutManager
Recyclerview를 커스텀 하는 한 가지 방법은 LayoutManager를 커스텀 하는 것입니다. LayoutManager는 아이템의 위치를 지정하고 측정하며 사용자에게 더 이상 표시되지 않는 항목을 재활용할 시기를 결정합니다.
다음은 Recycler View에서 중앙 item을 기준으로 확대/축소하는
CenterZoomLayoutManager
예시입니다.
LinearLayoutManager
를 상속하는 CenterZoomLayoutManager 클래스를 생성합니다.
generateDefaultLayoutParams()
에서 LayoutParams 개체를 생성합니다.
canScrollHorizontally()
을 true로 설정해 수평 스크롤을 지원합니다.
scrollHorizontallyBy()
에서 수평 스크롤에 대한 동작을 구현합니다.
추가로
smoothScrollToPosition()
에서 스크롤 속도를 커스텀 합니다.
마지막으로 Recyclerview에
setLayoutManager
로 적용합니다.
이렇게 하면 중앙 item을 기준으로 확대/축소되는 효과가 Recyclerview에 적용됩니다.
추가로 가운데 스냅 효과를 주기 위해
GravitySnapHelper
클래스를 생성해 Recyclerview에 적용합니다. GravitySnapHelper는 리스트가 스크롤 될 때 아이템을 화면 중앙으로 스냅 하는 효과를 주기 위한 클래스입니다.
다음은
GravitySnapHelper
예시입니다.
Gravity.CENTER
로 스크롤 효과 기준을 가운데로 설정하고,
attachToRecyclerView
를 사용해 Recyclerview에 적용합니다.
val snapHelper = GravitySnapHelper(Gravity.CENTER)
snapHelper.setScrollMsPerInch(25f) //scroll speed
snapHelper.attachToRecyclerView(recyclerView)
Custom Adapter
Adapter는 Recyclerview의 필수 항목으로 아이템 뷰를 생성하고 데이터를 바인딩 하는 역할을 합니다. Adpater를 커스텀 해 복잡한 레이아웃이 포함된 Recyclerview를 만들 수 있습니다.
다음은 중첩 Recyclerview를 만들기 위한 Adapter 커스텀 예시입니다. 수직 Recyclerview 내부에 여러 뷰 타입의 수평 Recyclerview가 배치된 경우입니다.
ParentAdapter와 ChildAdapter가 존재하며, ParentAdapter에서 데이터 뷰 타입에 따라 여러 개의 ViewHolder를 생성하고 바인드 합니다.
getItemViewType
에서 데이터의 뷰 타입을 구분합니다.
onCreateViewHolder
에서 뷰 타입에 따라 뷰 홀더를 생성합니다.
onBindViewHolder
에서 뷰 타입에 따라 각 뷰 홀더에 바인드 합니다.
ChildAdapter는 여러 개로 각각 사용해도 됩니다. 예제에서는 하나의 ChildAdapter를 이용했습니다. ChildAdapter item의 width와 layoutManager를 다르게 설정해 여러 레이아웃 타입을 만들었습니다.
추가로
RecycledViewPool
을 이용해 스크롤 성능을 개선합니다.
RecycledViewPool은 뷰 계층에서 일시적으로 제거된 뷰를 위한 저장소 풀입니다. 리스트의 각 항목에 대해 재사용할 수 있도록 하여 RecyclerView의 성능을 향상시키는 데 사용됩니다. 성능 향상을 위해 중첩된 Recyclerview에 하나의 RecycledViewPool을 공유합니다.
또한 LinearLayoutManager에
initialPrefetchItemCount
로 중첩된 Recyclerview에서 미리 가져와야 하는 내부 항목 수를 설정합니다. 기본 값 2 대신 설정한 항목 수만큼 미리 바인딩 작업을 수행해 스크롤 될 때 버벅거림을 방지할 수 있습니다.
(자세한 소스 코드를 보시려면 아래 링크를 클릭해 주세요.)
ParentAdapter 예제 코드
ChildAdapter 예제 코드
Nested RecyclerView Scrolling
중첩된 Recyclerview의 경우 스크롤이 잘되지 않는 경우가 발생할 수 있습니다. 부모 Recyclerview에서 스크롤 이벤트를 해결하는 동안 중첩된 다른 Recyclerview를 스크롤 하려고 하면 상호 간 터치 이벤트 간섭으로 인해 스크롤 동작이 잘 안 먹힐 수 있습니다. 이러한 경우
onInterceptTouchEvent
를 이용해 스크롤을 개선할 수 있습니다.
onInterceptTouchEvent()
메서드는 ViewGroup 표면에서 터치 이벤트가 감지될 때마다 호출됩니다. true를 반환하면 MotionEvent가 가로채기 되며, 이는 하위 요소로 전달되지 않고 상위 요소의 onTouchEvent() 메서드에 전달됨을 의미합니다.
MotionEvent.ACTION_DOWN : 손가락을 눌렸을 때
MotionEvent.ACTION_MOVE : 손가락을 누르고 움직였을 때
MotionEvent.ACTION_UP : 손가락 누른 것을 땠을 때
아래 예시는 onInterceptTouchEvent를 커스텀 한
OrientationAware RecyclerView
입니다. 사용자가 손가락을 움직일 때 스크롤 방향이 Recyclerview 방향과 일치하면 터치 이벤트를 가로채고 그렇지 않으면 가로채지 않습니다.