Android 메모리 성능을 고려한 중첩 레이아웃 내 스크롤 페이징 처리
NestedScrollView
와 같은 수직 방향으로 중첩된 레이아웃에서의 RecyclerView
스크롤 페이징 처리는 성능상 좋지 못한 결과를 초래하게 됩니다.
본 글에서는 위 문제를 해결하기 위해 NestedScrollView
를 사용하지 않는 방법으로 NestedScrollView
과 동일한 인터랙션과 부드러운 스크롤 페이징 기능을 구현하는 방법을 고민하고 문제를 해결 했던 경험에 대해 기술하였습니다.
NestedScrollView
는 같은 ViewGroup
내에 수직 방향의 RecyclerView
와 또 다른 수직 방향의 레이아웃을 중첩시킬 때에 사용하는 레이아웃입니다.

NestedScrollView
특성 중 하나는 레이아웃 생성 단계에서 NestedScrollView
의 전체 수직 스크롤 높이를 계산합니다.
이를 위해 중첩된 수직 방향의 RecyclerView
는 위 단계에서 모든 아이템이 생성됩니다.
이 같은 NestedScrollView
특성 때문에 스크롤 위치에 따라 기존에 바인딩된 아이템 뷰를 재활용하여 메모리 성능을 높이는 RecyclerView
의 장점을 활용하지 못하게 되고, 또한 이로 인해 Android Jetpack Paging 3 라이브러리를 제대로 사용할 수 없게 됩니다.
즉, NestedScrollView
내의 RecyclerView
는 메모리가 재활용되지 않아 전체 아이템의 개수에 비례하여 메모리가 누적되게 됩니다. 이 같은 상황에서 다행히 RecyclerView
의 전체 아이템 개수가 적으면 큰 상관이 없지만, 예를 들어 전체 아이템 개수가 100개 또는 그 이상, 그리고 이를 스크롤 페이징까지 구현해야 한다면 아마도 앱의 메모리 성능이 크게 저하되어 랙이 걸리는 현상을 볼 수 있을 것입니다.
하지만 이 같은 문제가 발생함에도 불구하고, 앱의 기획으로 인해 NestedScrollView
환경의 RecyclerView
에 스크롤 페이징을 구현해야만 한다면 CoordinatorLayout
을 활용하여 성능을 해결하면서 위 환경을 비슷하게 구현할 수 있습니다.
이를 설명하기에 앞서, CoordinatorLayout
와 함께 사용하는 AppBarLayout
하위 레이아웃인 CollapsingToolbarLayout
의 layout_scrollFlags
속성의 종류를 설명하자면 다음과 같습니다.
noScroll
- 스크롤을 해도 뷰가 사라지지 않게 합니다.
scroll
- 스크롤을 하면 뷰를 사라지게 합니다.
exitUntilCollapsed
- 위쪽으로 스크롤하는 동안은
minHeight
에 도달할 때까지만 뷰를 사라지게 합니다. - 아래쪽으로 스크롤하는 동안은 사라진 뷰를 나타나게 합니다.
enterAlways
- 위쪽으로 스크롤하는 동안은 뷰를 사라지게 하고, 아래쪽으로 스크롤하는 동안은 뷰를 다시 나타나게 합니다.
enterAlwaysCollapsed
- ‘
enterAlways
’와 함께 사용할 추가 플래그입니다, - 아래쪽으로 스크롤하는 동안 스크롤 범위의 끝에 도달했을 때만 뷰를 나타나게 합니다. 만일
minHeight
속성을 설정하면 그 값에 도달했을 때만 뷰를 나타나게 합니다.
snap
- 스크롤이 끝날 때, 뷰가 부분적으로만 표시되는 경우 뷰를 가장 가까운 가장자리로 스크롤합니다.
snapMargins
- ‘
snap
’과 함께 사용할 추가 플래그입니다. - 설정된 경우 뷰는 뷰 자체의 가장자리와 반대로 위쪽과 아래쪽 여백으로 스냅됩니다.
위와 같은 layout_scrollFlags
중 scroll
, enterAlways
, enterAlwaysCollapsed
플래그를 조합하면 NestedScrollView
환경과 같이, 위쪽으로 스크롤하는 동안은 상단 뷰를 사라지게 하고, 아래쪽으로 스크롤하는 동안 스크롤 범위의 끝에 도달했을 때만 상단 뷰를 다시 나타나게 하는 레이아웃을 구현할 수 있습니다.

위 그림의 오른쪽 레이아웃과 같이 리팩토링한 구조에서는 NestedScrollView
를 사용하지 않았기 때문에 스크롤 위치에 따라 기존에 바인딩된 아이템 뷰를 재활용하여 메모리 성능을 높이는 RecyclerView
의 장점을 살릴 수 있을 뿐만 아니라, Android Jetpack Paging 3 라이브러리도 십분 활용할 수 있게 됩니다.
하지만, 위와 같이 CoordinatorLayout
를 중첩시킬 때는 위쪽으로 스크롤 했을 때 1 Depth의 CoordinatorLayout
의 CollapsingToolbarLayout
가 접히지 않는 문제가 추가적으로 발생하게 됩니다.
그 이유는 2 Depth의 CoordinatorLayout
의 RecyclerView
app:layout_behavior=”@string/appbar_scrolling_view_behavior
” 가 2 Depth의 AppBarLayout
한테만 작동하기 때문입니다.

해당 문제를 해결하기 위해선 2 Depth의 CoordinatorLayout
에 대한 커스텀 뷰인 NestedCoordinatorLayout
을 개발해야 합니다.

이와 관련된 좋은 오픈소스가 있어 아래와 같이 첨부했고 부가적으로 주석을 달아보았습니다.
위 코드와 같이 NestedCoordinatorLayout
클래스에 CoordinatorLayout
을 상속받아 NestedScrollingChildHelper
를 이용하여 NestedScrollingChild2
인터페이스를 구현하게 되면 1 Depth의 CoordinatorLayout
의 CollapsingToolbarLayout
가 전부 접힌 이후에 2 Depth의 CoordinatorLayout
의 CollapsingToolbarLayout
가 접히게 되는 결과를 얻을 수 있습니다.

읽어주셔서 감사드립니다.
written by Henry (Jinwoo Kim)
email: jw_kim@fitpet.co.kr