Intro
목록이 중첩된 복잡한 디자인을 개발해야할 때가 많습니다.
- 상품 목록에서 최근 본 상품을 상단에 보여준다.
- 상품 중간에 광고를 넣는다.
- 판매자의 다른 상품들을 보여준다.
이런 경우 복잡한 화면을 어떻게 구현할지 고민해 봐야합니다.
먼저 View System 에서는 어떻게 개발해야하는지 알아봅시다.
2024 Google I/O Extended in Busan 에서 발표한 글을 토대로 정리한 내용입니다.
예시 앱을 만들면서 이해해봅시다. GDG Busan 의 앱을 한 번 만들어 보겠습니다. 아래와 같이 디자인하였습니다.
이 화면은 다음과 같은 순서로 구성되어있습니다.
- 최 상단 배너
- 운영진 목록 (Organizers)
- 이벤트 목록 (Past Events)
이제 Android View System 의 RecyclerView 를 사용해 만들어봅시다.
RecyclerView 란?
https://developer.android.com/develop/ui/views/layout/recyclerview
Android View System 에서 정적, 동적 목록을 그리기 위해 사용하며 findViewById 의 Cost 를 줄이기 위해 ListView 와 달리 ViewHolder 패턴을 강제합니다. LayoutManager 를 사용해 Linear, Grid, StaggeredGrid 형태의 뷰를 쉽게 구현할 수 있으며 Animation 전용 클래스도 제공합니다.
기본 구현 방식은 다음과 같습니다. RecyclerView.ViewHolder 를 상속하는 ViewHolder 를 구현하고 Adapter 에 해당 ViewHolder 타입을 전달합니다. 그 후 RecyclerView 의 adapter 로 set 하면 됩니다.
- Adapter 와 ViewHolder
- View 구조
스크롤 가능한 목록
이 리사이클러뷰를 사용해 간단하게 접근해 봅시다. 우리는 전체 화면을 스크롤하면서 동시에 목록도 스크롤해야합니다.
ScrollView 와 RecyclerView 를 같이 쓰면 되겠네요!
그러나 예상과 다르게 동작하는 것을 확인할 수 있습니다. 스크롤 뷰, 리사이클러뷰의 스크롤 방향이 겹치는 세로 스크롤은 리사이클러뷰 스크롤과 전체 스크롤이 따로 노는 것을 확인할 수 있습니다. 가로 스크롤은 잘 되네요.
이를 해결하기 위해 우리는 여러가지 방법을 사용할 수 있습니다.
1. NestedScrollView
2. Using Multi ViewType
3. ConcatAdapter
4. Epoxy,
5. Motion Layout
...
1. NestedScrollView
이 중에 가장 쉬운 것 하나만 알면 되지 않을까요? NestedScrollView 를 한 번 사용해봅시다. 변경은 매우매우매우 간단합니다. ScrollView 를 NestedScrollView 로 바꿔주기만 하면 됩니다.
이제 우리가 원하는 대로 잘 동작하는 것을 확인할 수 있습니다.
여기서 끝! ... 일 까요?
AndroidStudio 의 Layout Inspector 를 확인해보면 문제점을 발견할 수 있습니다. 스크롤 방향이 다른 vertical RecyclerView 는 재사용이 잘 되지만 방향이 겹친 Past Event 는 뷰가 재사용되지 않고 미리 다 그려버리는 것을 확인할 수 있습니다. NestedScrollView 는 매우 간단하지만 리사이클러뷰의 가장 큰 장점을 없애 버리죠. WrapContent 높이를 사용하면, 모든 자식 View들이 한 번에 Measure & Layout 되어야 합니다.
아이템 개수만큼 View 인스턴스를 모두 미리 생성하므로 성능 저하가 일어납니다. 구현이 매우 간단하기 때문에 아이템 개수가 적다면 충분히 고려해볼 수 있습니다만 아이템 개수가 많아질 가능성이 있다면 다른 방식으로 만들어야 합니다.
2. Using Multi ViewType
그럼 재사용 가능하게 만드는 방법은 없을까요? 앞에서 소개한 Multi-ViewType 방식을 사용해 봅시다. RecyclerView 는 ViewType 에 따라 다른 ViewHolder 를 사용하도록 할 수 있습니다. 이를 이용하면 ViewHolder 안에 RecyclerView 를 넣을 수도 있죠.
화면을 네 부분으로 나누어 뷰타입으로 지정해봅시다.
각 아이템 형태에 따라 필요한 데이터 형태의 Sealed interface 를 GdgBusanViewItem 이름으로 정의하였습니다. GdgBusanRecyclerView 는 4가지 뷰타입을 재사용하며 화면에 그리고 그 중 OrganizersViewHolder 는 그 내부에 RecyclerView 를 그립니다.
재사용이 가능해 리사이클러뷰의 장점이 그대로 적용됩니다. 또한, Layout 파일을 수정하지 않아도 아이템만 추가해주면 동일한 뷰를 몇 번이든 띄울 수 있습니다. 그러나 ViewTpye 이 많아질수록 구현이 복잡하고 시간이 오래 걸립니다.
3. ConcatAdapter
여기부터는 직접 구현해보지는 않고 설명으로 대체하겠습니다.
RecyclerView 1.2.0 부터 추가된 ConcatAdapter 를 사용해 구현하는 방법도 있습니다. ConcatAdapter 는 여러 Adapter 를 하나의 Adpater 로 합쳐서 구현할 수 있게 도와줍니다.
재사용 가능하며 Multi View Type 보다는 비교적 간단합니다. 그러나 아이템이 하나여도 어댑터로 구현해야 합니다. ConcatAdapter 의 생성자에 ConcatAdapter.Config 객체를 넘겨서 옵션을 제어할 수 있습니다. 옵션에는 ConcatAdapter.Config 는 isolateViewTypes 와 stableIdMode 가 있습니다.
isolateViewTypes 는 어댑터 간에 동일한 뷰홀더를 공유할 것인지 설정하는 옵션입니다. isolateViewTypes 가 true 이면 어댑터간에 풀을 공유하지 않습니다. false 이면 뷰홀더를 공유하게 되지만 서로 다른 뷰홀더가 동일한 뷰타입을 사용하지 않도록 주의해야합니다.
stableIdMode 는 3가지가 존재합니다.
- NO_STABLE_IDS: 기본으로 지정되는 값. concatAdapter 에 추가되는 어댑터의 stable id 를 무시한다.
- ISOLATED_STABLE_IDS: concatAdapter가 stable id 가 지정된 어댑터를 요구한다. 서로 독립적인 id 풀을 가지고 동일한 id 를 리턴할 수 있다.
- SHARED_STABLE_IDS: concatAdapter가 stable id 가 지정된 어댑터를 요구한다. 서로 id 풀을 공유하며 서로 다른 id 를 가지도록 보장해야한다.
이 옵션들에 대해 이해가 부족한 상태로 작업하면 원하지 않는 결과를 얻을 수도 있습니다.
ConcatAdapter 에 대해 더 자세한 내용은 잘 정리된 다음 글들을 추천합니다.
https://medium.com/androiddevelopers/merge-adapters-sequentially-with-mergeadapter-294d2942127a
https://medium.com/hongbeomi-dev/concatadapter-deep-dive-6aa79750f81e
4. Epoxy
Airbnb에서 만든 오픈소스 라이브러리로, RecyclerView 기반으로 다양한 UI를 쉽게 구성하도록 돕습니다. 주로 아래와 같은 이유로 많이 사용됩니다. 데이터 바인딩, DiffUtil, 여러 ViewType 처리 등을 Epoxy가 자동화/간소화해줍니다. 서로 다른 UI를 한 화면에서 쉽게 구성 가능하며, EpoxyModel 단위로 뷰를 정의하기 때문에 직관적입니다. 그러나 별도의 라이브러리를 도입해야 하며, 팀원 모두가 사용법을 숙지해야 합니다.
5. MotionLayout
MotionLayout 은 앱에서 모션 및 위젯 애니메이션을 관리하는 layout type 입니다. MotionLayout 과 RecyclerView 를 같이 사용해서스크롤 위치에 맞춰 상단 배너와 리사이클러뷰를 collapsing 한다면 동일한 화면을 구현할 수 있습니다. 그러나 더 복잡한 화면이면 한계가 있을 것으로 보입니다.
Outro
중첩 스크롤 가능한 RecyclerView? 그냥 Compose Lazy Layout 의 item, items 쓰면 해결되는거 아닌가요? 구현만 보자면 그렇습니다. 하지만 이미 리사이클러뷰, 리스트뷰로 수많은 복잡한 코드가 작성되어있죠. 일부만 수정하거나 컴포즈로 마이그레이션하거나 혹은 컴포즈를 아직 도입하지 않는 환경에서 개발하게 된다면 View system 에서 어떻게 개발할 수 있는지 알아야합니다.
그렇다면 우린 이것들 중 무엇을 사용해야할까요? 재사용할 수 없는 NestedScrollView 는 쓰면 안되는걸까요? 이것 또한 정답은 없습니다. 당장 빨리 릴리즈 해야하는데 성능이 중요하지 않을 수도 있습니다. 혹은 아이템 개수가 작아서 재사용이 필요없을 수도 있고요. 구현 방법을 이해하고 장단점을 숙지하고 있다가 본인의 상황에 맞춰서 사용하길 바랍니다.
'Android' 카테고리의 다른 글
[페스타고] 안드로이드에서 중첩된 페이지는 어떻게 개발해야 할까 (0) | 2024.06.17 |
---|---|
페스타고 상세 페이지 이동 애니메이션 변경 및 UX 개선 (0) | 2024.05.12 |
[Android] Encryption & Decryption in Android (0) | 2024.01.10 |
[안드로이드] DataStore 이해하기 (0) | 2024.01.08 |
RecyclerView 목록 스크롤에 CoordinatorLayout 적용하기 (0) | 2023.11.17 |