뷰 홀더가 무엇인지, 뷰 홀더 패턴이 무엇인지, 우리가 많이 사용하는 RecyclerView와 많이 사용했지만 현재는 사용성이 준 ListView와 차이는 무엇인지에 대해 쭉 한번 살펴 보겠다.
- ViewHolder 란
디벨로퍼를 참고하자면, RecyclerView 내 위치에 대한 아이템 뷰와 메타데이터를 설명한다고 나와있다(나머지 번역은 아래에 해놨다. 볼 사람은 참고하길 바란다.). 즉, 각 View를 보관하는 객체로, 각 구성 요소를 저장하여 반복적으로 조회하지 않고도 즉시 엑세스 할 수 있다 보면 된다.
- ViewHolder Pattern 이란?
데이터가 적을 경우, 빠르게 뷰의 구성 요소들을 findViewById()를 통해 조회해 적용시킬 수 있다. 하지만, 데이터가 많아질 경우 계속 findViewById()를 호출하다 보면 많은 비용을 요구(아래 설명 참고)하게 되어, 결국 호출을 이기지 못해 느려질 것이다.
이를 위해 나온 패턴이 ViewHolder Pattern이다.
각 뷰 객체를 뷰 홀더에 보관함으로써 findViewById()와 같은 반복적 호출 메서드를 줄여 효과적으로 속도 개선을 할 수 있는 패턴이다.
이를 사용하기 위해서는, ListView에서는 getView() 메서드 내에서 직접 호출을, RecyclerView에서는 RecyclerView.Adapter를 상속받으면 호출되는 onCreateViewHolder와 onBindViewHolder를 이용하면 된다. ListView와 RecyclerView의 차이는 뒤에서 알아보겠다.
간단히, 가장 많이 사용하는 RecyclerView에서 제공하는 메서드에 대해 살펴보고 넘어가자.
onCreateViewHolder() : ViewHolder를 새로 만들어야 할 때 호출되는 메서드이다. 이 메서드를 통해 각 아이템을 위한 XML 레이아웃을 이용한 뷰 객체를 생성하고 뷰 홀더에 담아 리턴한다. 이때는 뷰의 콘텐츠를 채우지 않는다. 왜냐하면 아직 ViewHolder가 특정 데이터에 바인딩된 상태가 아니기 때문이다.
onBindViewHolder() : ViewHolder를 데이터와 연결할 때 호출되는 메서드이다. 이 메서드를 통해 뷰홀더의 레이아웃을 채우게 된다.
- Android에서 ListView와 RecyclerView의 차이는?
리스트 형태의 뷰를 보여주기 위해 대표적으로 사용하는 뷰는 ListView와 RecyclerView이다. 우선 ListView에 대해 알아 보자.
ListView는 내가 보여주고자 하는 데이터들의 리스트를 보여주는 뷰이다. 하지만, 이는 위에 설명한대로, 데이터가 많아질수록 findViewById()의 지속적인 호출로 인해 스크롤이 느려지는 현상이 발생할 것이다. 이를 해결하기 위해 ListView에서 제공하는 getView() 메서드를 사용한다.
단순하게 view를 inflate하고, inflating된 뷰에서 findViewById()를 통해 원하는 뷰를 찾아 데이터를 셋해주는 작업은, 결국 데이터가 많아질수록 힘들다. 이는, getView() 메서드 내의 convertView를 이용하면 조금 편해지는데, 이를 이용해 처음 inflating를 한 후에는 그대로 view를 가져다 쓰는 방법이다. 조금 더 편해진 방법에서, 우리가 여태 공부한 뷰홀더 패턴을 사용한다면, 더욱 빠르게 뷰를 보여줄 수 있게 된다. 뷰홀더 패턴을 사용함으로써, 2번째 방식에서의 뷰를 inflating하는 작업을 줄였지만, findViewById()를 계속 호출해주는 작업은 그대로이기에, 이러한 작업들을 효율적으로 없애줄 수 있다. 뷰 홀더 패턴을 사용하면 한 번 연결한 데이터는 저장소에 저장하여 다시 findViewById를 통해 view를 찾아올 필요가 없어진다. 자세한 내용은 아래 첫 번째 참고를 살펴보면 된다.
이렇게 무거운 작업들을 최소화 시킴으로써, 뷰 재사용 등을 효율적으로 작성할 수 있게 되었다. 하지만! 가장 큰 단점은, 뷰홀더를 생성하는 코드를 깜박할 수 있다는 것이다. 어떻게 사람이 매번 기억할까… 그리하여 나온 것이 ViewHolder를 강제 구현시키도록 해주는 RecyclerView이다. ViewHolder를 강제로 구현하게 만듬으로써, 위의 단점을 효율적으로 제거했고, 현재는 가장 많이 사용하는 ViewGroup 중 하나가 됐다.
즉, ListView와 RecyclerView의 차이는, 뷰홀더 패턴을 강제로 구현해줬냐 직접 구현하냐의 차이가 될 것이다. (물론 이것만 있는게 아닐 수도 있다…)
Android Developer 에서 말하는 RecyclerView의 ViewHolder는 다음과 같다.
- RecyclerView 내 위치에 대한 아이템 뷰와 메타데이터를 설명한다.
RecyclerView.Adapter 구현은 ViewHolder를 하위 클래스로 만들고, 잠재적으로 비쌍 View.findViewById(int) 결과를 캐싱하기 위한 필드를 추가해야 한다.
RecyclerView.LayoutParams는 RecyclerView.LayoutManager에 속하는 반면, ViewHolder는 adapter에 속한다. Adapter는 뷰 컨텐츠를 보다 쉽게 바인딩할 수 있는 데이터를 저장하기 위한 자신만의 커스텀 ViewHolder 구현을 사용하는데 자유롭다.
구현은 개별 아이템 뷰가 ViewHolder 개체에 강한 참조를 보유하고, RecyclerView 인스턴스가 캐싱 목적으로 추가 오프-스크린 아이템 뷰에 대한 강한 참조를 보유할 수 있다고 가정해야 한다.
findViewById()가 왜 비용(cost)가 큰 작업일까?
- View.class 에서 확인해보면, 코드 상 큰 문제는 없다.
- 하지만 ViewGroup.class에서 보면,
- 위와 같다. 매 번 childrenView를 모두 확인해 가져오기 때문에, 비용이 크다고 말하는 것이다.
inflate : “부풀다, 팽창하다.” 라는 뜻을 가지고 있으며, 뷰를 팽창시킨다. 부풀리게 한다 라는 의미로 간단히 설명하자면, xml에 쓰여 있는 view 정의를 실제 view객체로 만드는 역할을 수행한다, 설정해 놓은 xml코드를 뷰로 만들어 가져다 쓸 수 있게 한다, 라는 의미로 알아보면 될 것 같다. 풍선도 바람을 불어 넣어 팽창시켜야 제 몫을 하듯 말이다.