안드로이드 ListView 알아보기. _ convertView와 ViewHolder

이유정·2022년 5월 23일
1

들어가기 전에 :

convertView :
ListView adapter에서 이전 뷰가 재사용이 가능할 경우, 그 뷰 정보.

ViewHolder :
Inflate를 최소화하기 위해서 view들의 각 요소에 바로 접극할 수 있게 저장해두고 사용하기 위한 객체.

inflate() : 부풀게 하다는 뜻으로, 안드로이드에서는 xml에 표기된 레이아웃들을 메모리에 객체화 시키는 행동을 일컫음.
-> xml의 코드를 객체화하여 코드에서 UI 요소 등을 사용하기 위해 inflate. _ setContentView() 함수에서 해당 동작이 이루어짐.

ListView :

listView란 view들을 리스트처럼 보여주는 컨테이너(뷰그룹)이다. 이때 listView의 경우 viewholder선택 사항(권장)인데, 이 이유와 관련 동작에 대해서 정리해볼려고 한다.

동작 :

listView는 화면에 보이는 view들만 생성해 보여주고, 스크롤 동작 등으로 안보이게 되는 기존의 view를 재활용하여 새 view를 구성해 보여주는 식으로 설계되었다. (스크롤 해서 첫번째 아이템이 안보이게 된다면, 이게 사라진 것이 아니라 해당 뷰를 재활용해 아래에서 나타날 아이템의 값만 다시 셋팅해 보여주는 형식) _ 성능을 고려!

사실 view를 각각의 모든 아이템에 대하여 매번 inflate해주는 것은 매우 무거운 작업이었기에, 이렇게 뷰를 재사용해주어, 기존 뷰에서 데이터 값만 새로 셋팅해준다면 메모리 측에서 부담이 적어진다.

그리고 "뷰를 재사용" 한다는 측면에서 성능을 보다 개선하는 방법이 2개 있는데, 하나가 convertView를 이용하는 것이고, 다른 하나는 viewHolder를 사용하는 것이다.

1. ConvertView _ inflate 횟수 줄이기.

사실 화면에 아이템을 보여줄 때, listView에서는 재활용 뷰라 볼 수 있는 convertView의 배열을 이용하여 아이템을 관리한다. _ adapter의 getView()에서 convertView 매개변수로 이용가능.

즉, Adapter에서는 스크롤을 통해, listView에서 화면 밖의 새 아이템을 가져올 때 getView()를 호출한다. 그리고 호출될때 처음에 화면의 아이템 수 만큼 convertView를 생성, 이후 호출에선 convertView를 재활용하는 식이다.

이용방식은 간단하다. getView(position, view, parent)의 매개변수 중 view가 convertView의 정보를 담고 있다.
만약 convertView가 null이 들어온 경우라면, view를 inflate하고 데이터를 셋팅, null이 아닌 경우라면 inflate 해주지 않고 데이터만 다시 셋팅해주면 된다.

그러면 매번 inflate해주지 않아도 되기에 메모리에 부담이 덜하게 된다.

  //Adapter _ getView() 코드
   override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
  
       if(convertView == null){ // null인 경우만 inflate해주기~
            convertView = LayoutInflater.from(context).inflate(...)
       } 

       val title = convertView.findViewById(...)
       title.text = .....
   
       return convertView
 }

여기서 위 코드의 문제점. :

view는 재활용하는데, 정작 데이터 셋팅할 때 convertView의 자식뷰(textView 등) 정보를 매번 findViewById()를 통해 다시 가져와 재연결함 >> 비효율적

2. ViewHolder _ findViewById() 줄이기

위의 문제점 해결을 위해 ViewHolder 패턴을 적용 할 수 있다.

여기에는 view의 Tag를 이용 _ 각 viewholder를 한번씩 생성한 후 view의 tag에 홀더를 저장할 경우, listView의 뷰가 재활용 될 때(convertView), viewHolder의 정보도 함께 불러올 수 있음으로 findViewById를 해서 자식뷰를 불러올 필요가 사라진다.

+) findViewById() : 이때 findViewById()는 ViewGroup 밑에 있는 모든 뷰들을 전부 한 번씩 순회하며 id값을 비교하는 과정을 거치기 때문에 자원이 많이 든다.

//Adapter _ getView() 코드
  override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
 	  var holder : MyViewHolder
      if(convertView == null){ // null인 경우만 inflate해주기~
           convertView = LayoutInflater.from(context).inflate(...)
            holder = ViewHolder()
            holder.title = converView.findViewById(...)
            convertView.tag = holder
      } else {
      		holder = convertView.tag as MyViewHolder
      }

	   //findViewById() 없이 홀더에서 정보가져와서 데이터 셋팅.
      holder.title?.text = .....
  
      return convertView
 }

 class MyViewHolder{
      var title : TextView? = null
 }

이렇게 converview의 tag에 viewHolder 넣어주면, convertView가 null이 아닐때 viewholder 정보도 가져오면서, 성능 향상이 가능함. >> 좀 더 매끄러운 스크롤 가능.

결론

따라서 listView의 경우 convertView와 ViewHolder를 이용해 무거운 작업을 최소화시키고, 재활용을 통해 성능을 최적화할 수 있다. listView를 쓴다면, 이를 이용해 사용하자.

+) 하지만 listView의 경우 viewholder가 강제사항이 아니기에, 공식 차원에서 viewholder를 쓰도록 프레임워크 단에서 강제한 것이 RecyclerView이다. (뿐만 아니라 기본 애니메이션 지원, 수직 수평 스크롤 지원 등의 기능 추가 등등 여러가지를 추가한 버전이라 볼 수 있음)

// 분명 recyclerView랑 viewholder 좀 자세히 알아볼 생각으로 시작했는데 약간 일이 커져서 이야기가 길어짐. -> 이제 recyclerView 공부한거 정리해보자.

참고 사이트 :
https://developer.android.com/reference/android/widget/ListView
https://itmining.tistory.com/2
https://heepie.me/77

profile
개인 공부 블로그

0개의 댓글