[Android, Double Scroll] 이중 스크롤 어떻게 구성하면 좋을까?
안녕하세요. 블랙진입니다.
ScrollView 안에 ListView 혹은 RecyclerView 가 있는 이중 스크롤에 관한 포스팅입니다.
cf) 제가 사용한 RecyclerView 예제는 이 링크에서 받을 실 수 있습니다.
1. 처음 생각이 드는 ScrollView 안에 RecyclerView 구조
preview
xml code
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:background="@android:color/holo_blue_bright"
android:layout_width="match_parent"
android:layout_height="300dp">
<TextView
android:layout_centerInParent="true"
android:text="BlackJin Tistory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</ScrollView>
ScrollView 안에는 자식뷰를 단 1개만 가질 수 있습니다. 이에 LinearLayout 을 설정하여 TextView 1개 있는 레이아웃과 그 아래 RecyclerView를 달아보았습니다. 어떻게 동작을 할까요?
BlcakJin Tistory 부분을 스크롤 하면 ScrollVIew 가 동작되고 RecyclerView 를 스크롤 하게 되면 RecyclerView 의 스크롤이 움직입니다.
1. 처음 앱을 시작 할 때 상단뷰의 일부가 가려저 있습니다. (리사이클러뷰의 일부가 위로 올라와 있다)
이는 RecycleView 에서 아이템을 그리는 과정에서 아이템에 포커싱이 되어 ScrollView 가 말려 올라가는 현상입니다. 리사이클러뷰로 가는 포커싱을 막아줌으로서 해결할 수 있습니다.
<!-- 자식뷰의 포커싱을 막아주는 blocksDescendants 를 추가해 줍니다. -->
<LinearLayout
android:descendantFocusability="blocksDescendants"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:descendantFocusability
Defines the relationship between the ViewGroup and its descendants when looking for a View to take focus.
Must be one of the following constant values.
Constant | Value | Description |
---|---|---|
afterDescendants | 1 | The ViewGroup will get focus only if none of its descendants want it. |
beforeDescendants | 0 | The ViewGroup will get focus before any of its descendants. |
blocksDescendants | 2 | The ViewGroup will block its descendants from receiving focus. |
AndroidVIewGroup - blacksDescendants
2. ScrollView 와 RecyclerView 의 스크롤이 따로따로 동작한다.
이중 스크롤에서 가장 큰 문제가 바로 각각의 스크롤이 따로 동작한다는 것입니다. 이를 알아서 해결해 주는 NestedScrollView 를 사용해 주면 됩니다.
NestedScrollView 에 관한 구글 문서에는 이렇게 적혀 있습니다.
NestedScrollView is just like ScrollView
, but it supports acting as both a nested scrolling parent and child on both new and old versions of Android. Nested scrolling is enabled by default.
ScrollView 와 동작은 같으나 부모와 자식 둘다 중첩된 스크롤링 액션을 지원해준다!
동작원리에 관해 궁금하신 분은 이에 관한 영문 포스터가 있으니 참고해 주세요.
3. Solution
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:descendantFocusability="blocksDescendants"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:background="@android:color/holo_blue_bright"
android:layout_width="match_parent"
android:layout_height="300dp">
<TextView
android:layout_centerInParent="true"
android:text="BlackJin Tistory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
두가지 Solution을 모두 적용해 위와 같이 작업해 주면 이중 스크롤뷰 안에 리사이클러뷰를 구현 하실 수 있습니다.
cf) 이중 스크롤뷰 안에 리사이클러 뷰를 사용하게 되면 리사이클러뷰는 아이템을 전부 미리 생성하게 됩니다. (RecyclerView Adapter 의 onBindViewHolder 에서 로그를 찍어보시면 확인하실 수 있습니다.) 또한 Item View 를 재활용 하지 않기 때문에 View 를 재사용하여 메모리 효율을 높일 수 있다는 리사이클러뷰의 큰 이점을 잃어버리게 되니 꼭 참고해서 사용해 주셔야 합니다. 아이템이 많은 경우에는 사용을 지양합니다.