이 글은 PC 버전 TISTORY에 최적화 되어있습니다.
서론
안드로이드는 진화하는 플랫폼입니다. 5.0(롤리팝) 버전이 ListView 보다 유연하고 성능이 향상된 RecyclerView와 함께 발표되었습니다. 구글에서 밀고 있는 Material Design의 일환으로 List를 표시해주는 ListViewd, GridView도 Material Design을 따라야 했습니다. 하지만 기존의 ListView는 커스터마이징 하기에 힘들었고, 구조적인 문제로 성능상의 문제도 있었습니다. RecyclerView는 ListView의 문제를 해결하기 위해 개발자에게 더 다양한 형태로 커스터마이징 할 수 있도록 제공되었습니다. RecyclerView와 ListView의 가장 큰 차이점은 Layout Manager와, View Holder 패턴의 의무사용, Item에 대한 뷰의 변형이나 애니메이션할 수 있는 개념이 추가되었습니다. 리스트 뷰의 성능 상의 이슈도 해결해주면서, 많은 타입의 뷰들을 가독성 있게 보여줄 수 있는 리사이클러 뷰에 대해서 알아보도록 하겠습니다.
주요 클래스
- Adapter – 기존의 ListView에서 사용하는 Adapter와 같은 개념으로 데이터와 아이템에 대한 View생성
- ViewHolder – 재활용 View에 대한 모든 서브 뷰를 보유
- LayoutManager – 아이템의 항목을 배치
- ItemDecoration – 아이템 항목에서 서브뷰에 대한 처리
- ItemAnimation – 아이템 항목이 추가, 제거되거나 정렬될때 애니메이션 처리
Adapter
리스트 뷰는 데이터가 어디서 왔냐에 따라 BaseAdapter를 상속한 ArrayAdapter (배열로부터 데이터 가져올 때 사용), CursorAdapter (DB로부터 가져올 때), SimpleAdapter(XML 등으로부터 가져올 때)를 구분하여 사용합니다. 하지만 리사이클러 뷰는 Universal한 Adapter를 사용하여 데이터 소스를 처리합니다. 이것은 리사이클러 뷰의 유연성을 보여줍니다. 다음의 3가지 인터페이스를 구현해야합니다.
- onCreateViewHolder(ViewGroup parent, int viewType) : 뷰 홀더를 생성하고 뷰를 붙여주는 부분입니다.
- onBindViewHolder(ListItemViewHolder holder, int position) : 재활용 되는 뷰가 호출하여 실행되는 메소드, 뷰 홀더를 전달하고 어댑터는 position 의 데이터를 결합시킵니다.
- getItemCount( ) : 데이터의 개수 반환
당신의 앱의 성능을 ViewHolder 패턴을 이용해 향상 시키세요
리스트뷰에서는 뷰홀더 패턴을 권장했습니다. UI 를 수정할 때 마다 부르는 findViewById( ) 를 뷰홀더 패턴을 이용해 한번만 함으로서 리스트 뷰의 지연을 초래하는 무거운 연산을 줄여주었습니다. 이 문제를 리사이클러 뷰에서는 뷰 홀더 패턴을 꼭 사용하도록 함으로서 해결했습니다.
하지만 뷰홀더 패턴을 기적이라고 생각하면 오산입니다. 실제로 당신의 앱의 퍼포먼스를 향상시켜주지만 오래된 기계일 수록 더 향상시켜 준다고 보시면 됩니다. 다른 말로 최신의 파워풀한 디바이스는 뷰 홀더 패턴을 사용하지 않은 리스트 뷰나 리사이클러 뷰의 성능 차이는 미세하기 때문입니다.
간과하기 쉬운 중요한 점은 뷰홀더 패턴을 사용한 리스트뷰와 리사이클러뷰의 성능은 같습니다. 단지 차이점은 리사이클링뷰는 뷰홀더 패턴이 강제되는 것일 뿐입니다. 이전의 리스트 뷰는 선택적이었지만 성능 차이가 너무 컸기 때문에 변화된 것으로 생각됩니다.
LayoutManager을 이용해 풍선 껌처럼 유연해지세요
리스트 뷰는 수직 스크롤만 가능합니다. 리스트뷰를 수평으로 사용할 수는 없었죠. 그것을 구현하기 위한 방법이 몇가지 있다는 것을 알고는 있지만 리스트 뷰는 그렇게 동작하도록 디자인 되지 않았습니다. 그러나 이제 리사이클러뷰에서는 수평 스크롤 또한 지원합니다. 뿐만 아니라 더 다양한 타입의 리스트들을 지원하고, 커스텀 할 수 있도록 해줍니다. 많은 타입의 리스트를 사용하기 위해선 RecyclerView.LayoutManager 클래스를 사용하면 됩니다. 리사이클러 뷰는 아래와 같은 3가지의 미리 정의된 Layout Managers를 제공합니다.
- LinearLayoutManager : 리사이클러 뷰에서 가장 많이 쓰이는 레이아웃으로 수평, 수직 스크롤을 제공하는 리스트를 만들 수 있습니다.
- StaggeredGridLayouManager : 이 레이아웃을 통해 뷰마다 크기가 다른 레이아웃을 만들 수 있습니다. 마치 Pinterest 처럼요.
- GridLayoutManager : 여러분의 사진첩 같은 격자형 리스트를 만들 수 있습니다.
사용 예시
mActivity = getActivity();
mRecyclerView = (RecyclerView) mActivity.findViewById(R.id.recycler_view);
mLinearLayoutManager = new LinearLayoutManager(mActivity.getApplicationContext());
mRecyclerView.setLayoutManager(mLinearLayoutManager);
Item Decoration
리스트 뷰에서는 아래의 파라미터를 XML에 추가함으로서 쉽게 divide할 수 있었습니다. 리사이클러 뷰에서는 RecyclerView.ItemDecoration 클래스를 통해 divider를 원하는 아이템에 추가해주게 되었습니다. 조금 복잡해졌지만 동적인 데코레이팅이 가능해졌습니다.
Item Animator을 이용해 멋진 앱을 만드세요
Metarial Design에 대해 조명된 이후로 리스트에서의 애니메이션은 무궁무진한 가능성을 가지게 되었습니다. 리스트 뷰에서는 아이템의 삽입이나 삭제에 특별한 애니메이션이 없었습니다. 하지만 리사이클러 뷰에서는 RecyclerView.ItemAnimator 클래스를 통해 애니메이션을 핸들링 할 수 있게되었죠. 이 클래스를 통해서 아이템 삽입, 삭제, 이동에 대한 커스터마이징이 가능하고, 또한 DefaultItemAnimator가 제공되므로 커스터마이즈가 필요 없이 사용할 수도 있습니다. notifyItemChanged(int position), notifyItemInserted(int position), notifyItemRemoved(int position)을 ItemAnimator을 통해 특정 아이템에 대한 애니메이션을 발생시킬 수 있습니다.
클릭으로 인한 중첩을 OnItemTouchListener 로 피하세요
리스트뷰는 쉽게 클릭을 감지할 수 있었습니다. AdapterView.OnItemClickListener 를 이용해서요. 하지만 RecyclerView.OnItemTouchListener은 리사이클러 뷰의 터치 이벤트를 감지합니다. 좀 복잡하지만 개발자에게 터치 이벤트를 인터셉트하는 제어권한을 주게되었죠. 안드로이드 공식 문서에서는 터치 이벤트를 인터셉트함으로서 리사이클러 뷰에게 전달하기 전에 조작함으로서 유용하게 사용될 수 있다고 합니다. 터치 이벤트를 통해 사용자가 아이템을 클릭 했는지 롱클릭 했는지를 직접 처리해주거나, ViewHolder에서 OnClickListener를 직접 달아주어 처리해야합니다.
결론
리사이클러 뷰는 리스트 뷰에 비해 상당히 기능적 커스터마이징에 중점을 두고 있습니다. 리스트 뷰에 비해 조금 어려울 수 있지만, 사용자에게 앱에 대한 연결성을 유지시켜주는 머터리얼 디자인에 대한 요구를 충족시키는 복잡한 그리드나 리스트에 아주 유용하게 쓰일 수 있겠죠. 다음 포스팅에선 유연성과 확장성이 향상된 Recycler View를 사용해보도록 하겠습니다.
참고 사이트
'Frontend > Android' 카테고리의 다른 글
[안드로이드] Handler 사용법 (4) | 2016.07.16 |
---|---|
[안드로이드] 다른 액티비티의 함수 호출 방법(오류) -> 싱글톤 패턴 (2) | 2016.07.14 |
[안드로이드] RecyclerView 예제 (17) | 2016.07.12 |
[안드로이드] 안드로이드 메모리 관리 (Weak Reference와 Soft Reference) (0) | 2016.07.08 |
[안드로이드] AsyncTask란? (개념 및 사용법) (3) | 2016.07.07 |
[안드로이드] runOnUiThread란? (개념과 사용법) (5) | 2016.07.07 |