본문 바로가기
Main

[Android] Custom Recyclerview

by ca.rrot 2023. 1. 10.

목차

1. Custom LayoutManager
2. Custom Adapter
3. Nested RecyclerView Scrolling

 

 

Recyclerview는 많은 데이터를 제한된 영역 내에서 유연하게 스크롤 가능한 리스트로 표시해 주는 위젯입니다. Recyclerview는 뷰를 재활용하므로 앱 성능과 메모리 관리 측면에서도 매우 유용하며, 대규모 데이터를 효율적으로 쉽게 표시할 수 있습니다. 안드로이드 UI 툴킷의 중요한 구성 요소이며 많은 안드로이드 앱에서 널리 사용되고 있습니다.

 

Recyclerview를 사용할 때의 이점 중 하나는 커스텀이 매우 용이하다는 것입니다. 이 포스팅에서는 사용자의 특정 요구에 맞게 커스텀 하는 몇 가지 방법을 살펴보겠습니다.

 

Custom LayoutManager

Recyclerview를 커스텀 하는 한 가지 방법은 LayoutManager를 커스텀 하는 것입니다. LayoutManager는 아이템의 위치를 지정하고 측정하며 사용자에게 더 이상 표시되지 않는 항목을 재활용할 시기를 결정합니다.

 

다음은 Recycler View에서 중앙 item을 기준으로 확대/축소하는 CenterZoomLayoutManager 예시입니다.

 

 

  1. LinearLayoutManager를 상속하는 CenterZoomLayoutManager 클래스를 생성합니다.
  2. generateDefaultLayoutParams()에서 LayoutParams 개체를 생성합니다.
  3. canScrollHorizontally()을 true로 설정해 수평 스크롤을 지원합니다.
  4. scrollHorizontallyBy()에서 수평 스크롤에 대한 동작을 구현합니다.
  5. 추가로 smoothScrollToPosition()에서 스크롤 속도를 커스텀 합니다.
  6. 마지막으로 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 방향과 일치하면 터치 이벤트를 가로채고 그렇지 않으면 가로채지 않습니다.

 

 

 

 

 

방문해 주셔서 감사합니다^^

잘 읽으셨다면 좋아요❤ 꾹!

오늘도 좋은 하루 보내세요 :)

댓글