[안드로이드 / Kotlin] RecyclerView에 대한 고찰

2022. 1. 22. 22:14·Android
반응형

안드로이드 개발에 있어 RecyclerView는 빼놓을 수 없는 요소가 되었습니다. 그 이유는 기존에 사용됐던 ListView에 비해RecyclerView 성능 및 효율이 좋기 때문입니다. RecyclerView를 정의하면 아래와 같습니다.

 

RecyclerView란? 사용자가 관리하는 많은 수의 데이터 집합(Data Set)을 개별 아이템 단위로 구성하여 화면에 출력하는 뷰그룹(ViewGroup)이며, 한 화면에 표시되기 힘든 많은 수의 데이터를 스크롤 가능한 리스트로 표시해주는 위젯입니다.

이제부터 기본적인 RecyclerView의 사용법 및 여러가지 기능에 대해 알아보도록 하겠습니다.

 


 

<소스코드 1>

class MyAdapter:
    RecyclerView.Adapter<MyAdapter.MyViewHolder>() {

    var items: ArrayList<MyData> = ArrayList()

    inner class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {

        fun setData(data: MyData) {
        	// itemView를 이용해 데이터 세팅
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val itemView = LayoutInflater.from(parent.context)
            .inflate(R.layout.item, parent, false)

        return MyViewHolder(itemView)
    }

	// ViewHolder에 데이터 전달 및 position별 데이터 결합
    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.setData(items[position])
    }

	// 반환할 데이터 개수
    override fun getItemCount(): Int {
        return items.size
    }
}

RecyclerView의 핵심은 Adapter와 ViewHolder 에 있습니다. RecyclerView에서는 ViewHolder 패턴이 강제되기 때문에 뷰의 재활용을 통해 성능을 개선할 수 있습니다. 간단히 주요 메소드에 대해 알아보면

  • onCreateViewHolder(ViewGroup parent, int viewType) : viewType 형태의 아이템 뷰를 위한 뷰홀더 객체를 생성.
  • onBindViewHolder(ViewHolder holder, int position) : position에 해당하는 데이터를 뷰홀더의 아이템 뷰에 표시.
  • getItemCount() : 전체 아이템 개수를 리턴.

<소스코드 2>

val rv = findViewById<RecyclerView>(R.id.recycler_view)
val adapter = MyAdapter(this)

// adapter에 사용할 데이터 세팅
// adapter.items = ...

rv.adapter = parentAdapter	// set adapter
rv.layoutManager = LinearLayoutManager(this)	// vertical? horizontal?

Adapter와 ViewHolder를 생성했다면 마지막으로 RecyclerView 객체를 생성하고 만든 Adapter를 연결만 해주면 사용할 수 있습니다. 추가적으로 LayoutManger를 통해 스크롤 방향을 지정할 수 있고, Item Decoration을 이용해 구분선이나 간격등 아이템에 대한 동적인 decoration이 가능합니다. 

 


 

여기까지가 RecyclerView에 대한 간단한 사용법입니다.

이 뒤부터는 RecyclerView를 좀 더 효율적으로 사용하는법에 대해 소개하겠습니다. 저같은 경우 이부분을 간과하고  RecyclerView를 사용했던 것 같습니다. 비록 요즘들어 디바이스의 성능이 점점 좋아져서 큰 성능의 개선을 느끼지 못할 수도 있지만, 현재 많은 앱에서 수많은 데이터의 사용이 이뤄지기 때문에 중요한 대목이라고 생각합니다.

 


 

1. RecyclerView의 갱신

 

흔히 RecyclerView에서 데이터 갱신이 이뤄질때 notifyDataSetChanged()를 많이 호출하곤 합니다. 하지만 이는 리스트 내 모든 데이터를 초기화 하기때문에 불필요한 뷰도 다시 그려질 수 있어 비효율적입니다. 따라서 아래와 같은 메소드를 상황에 맞춰 사용하시면 성능개선에 도움을 줄 수 있습니다!

  • 갱신될 위치를 알고있을때
     - notifyItemChanged(int position) : position 위치에만 다시 그림.
     - notifyItemRangeChanged(int positionStart, int itemCount) : position 위치부터 itemCount만큼 다시 그림.
     - notifyItemRangeInserted, notifyItemRangeRemoved 등..
  • 갱신될 위치를 모를때
     - DiffUtil 을 사용해 리스트의 변경된 부분만 갱신.

 

 

2. setHasFixedSize

 

recyclerView.setHasFixedSize(true)

setHasFixedSize()는 RecyclerView의 크기를 고정하겠다는 의미로 해당 함수를 true로 설정하게 되면 Adapter 내용이 바뀌어 갱신될때 RecyclerView.onMeasure에서 변경된 크기 값을 알아내는 로직없이 갱신이 이뤄질 수 있습니다. 

(Adapter의 내용에 따라 크기가 변경된다면 false로 설정합니다.)

 

 

 

3. setItemViewCacheSize

 

recyclerView.setItemViewCacheSize(int size)

setItemViewCacheSize()는 스크롤되어 화면에 사라지는 뷰에 대해 재사용되는 recycled view pool에 들어가지 않고, Cache에 저장되어 다시 화면에 나타날때 onBindViewHolder() 호출 없이 그대로 보여주게 해줍니다.

 

어? 그렇다면 ViewHolder를 재사용하는 RecyclerView 구조상 같은 맥락 아닌가라는 생각이 들 수 있습니다. 여기서 포인트는 RecyclerView는 ViewHolder 객체를 재사용하는 것이지, 객체 내 데이터까지 재사용하지는 않습니다. 따라서 위 메소드 호출이 없다면 onBindViewHolder()의 재호출이 발생합니다!

 

 

 

4. setHasStableIds

 

adapter.setHasStableIds(true)

setHasStableIds()를 true로 설정하게 되면 같은 id를 가지는 아이템을 onBindViewHolder()의 재호출 없이 최초 만든 View를 보여줍니다. 

단, 해당 메소드를 사용하기 위해 Adapter내 getItemId()를 상속받아야합니다! (id로는 고정값을 설정하시면 됩니다.)

override fun getItemId(position: Int): Long {
    return item[position].id.toLong();
}

 

 

 

5. 중첩된 RecyclerView에서의 Pool 공유

 

class OuterAdapter:
    RecyclerView.Adapter<OuterAdapter.OuterViewHolder>() {
	
    val sharedPool = RecyclerView.RecyclerViewPool()
        
    ...
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OuterViewHolder {
        val itemView = LayoutInflater.from(parent.context)
            .inflate(R.layout.item, parent, false)
            
        val innerRV = itemView.findViewById(R.id.inner_recycler_view)
        innerRV.setRecycledViewPool(outerRV.recycledViewPool)
        innerRV.layoutManager = LinearLayoutManager(context)
        	.apply { recycleChildrenOnDetach = true }

        return MyViewHolder(itemView)
    }
    
    ...

동일한 뷰에 대해 중첩으로 RecyclerView가 구성되있을때 바깥 쪽 RecyclerView의 Pool을 안쪽 RecyclerView Pool에 공유해 성능 높여줍니다. setRecyclerViewPool()을 이용해 바깥 쪽 RecyclerViewdml Pool을 공유하며, 추가적으로 LayoutManager 설정에서 recycleChildrenOnDetach()를 true로 설정합니다.

 

 

 

6. 초기화는 ViewHolder에서만..!

 

onBindViewHolder()에서는 아이템 위치(position)를 주기 때문에 무심코 리스너 설정을 해당 메소드에서 구현할 수 있습니다. 하지만 RecyclerView의 구조상 ViewHolder는 재사용하나 onBindViewHolder()는 재호출이 이뤄지기 때문에 비효율적인 코드가 될 수 있으므로, onBindViewHolder()에서는 데이터 전달만하고 리스너 설정은 ViewHolder 내에서 구현하면 성능 개선에 도움이 될 수 있습니다.

 

그렇다면 ViewHolder내에서 아이템의 위치는 어떻게 알 수 있을까요? 다행히 getAdapterPosition()이라는 메소드를 지원하기 때문에 충분히 구현할 수 있습니다!

inner class MyViewHolder(itemView: View):
        RecyclerView.ViewHolder(itemView) {

        var binding = ItemBinding.bind(itemView)

        init {
            // 리스너설정은 최초 초기화시에만
            // onBindViewHolder()에서 하게되면 불필요한 리스너 설정이 지속됨.
            binding.container.setOnClickListener {
                if(adapterPosition != RecyclerView.NO_POSITION) {
                    val item = items[adapterPosition]
                    ...
                }
            }
        }
        
        ...

 


 

오늘은 RecyclerView에 대해 간단하게 남아 알아보았습니다. 무심코 사용했던 RecyclerView에 대해 자세히 알아보니 여러 다양한 기능이 있었고, 추가적으로 성능 개선을 할 수 있는 방법이 있다는 사실을 알 수 있었습니다. 비록 짧은 식견과 부족한 실력이지만 도움되셨다면 다행입니다.

감사합니다:)

 

 

* 참고 : https://jaeryo2357.tistory.com/70

 

[Android] 뷰의 성능 개선 - RecyclerView

들어가기 전 이번 포스팅에서는 Android 앱에서 가장 많이 사용한다고 볼 수 있는 RecyclerView의 성능 개선에 대해서 알아보자. 하지만 먼저 알아야할 도구가 있다. 바로 GPU 렌더링 막대기 개발자 옵

jaeryo2357.tistory.com

https://recipes4dev.tistory.com/154

 

안드로이드 리사이클러뷰 기본 사용법. (Android RecyclerView)

1. 안드로이드 리사이클러뷰(RecyclerView) 리사이클러뷰(RecyclerView)는, "많은 수의 데이터 집합을, 제한된 영역 내에서 유연하게(flexible) 표시할 수 있도록 만들어주는 위젯"입니다. [안드로이드 개발

recipes4dev.tistory.com

     

반응형

'Android' 카테고리의 다른 글

[안드로이드 / Kotlin] 저전력 블루투스 BLE(1), BLE란?  (0) 2022.02.27
[안드로이드 / Kotlin] 진동  (0) 2022.02.13
[안드로이드 / Kotlin] Retrofit2  (0) 2022.01.15
[안드로이드 / Kotlin] 생체 인식 인증 사용하기  (0) 2022.01.07
[안드로이드 / Kotlin] 카카오 로그인 SDK v2 사용하기  (0) 2022.01.07
'Android' 카테고리의 다른 글
  • [안드로이드 / Kotlin] 저전력 블루투스 BLE(1), BLE란?
  • [안드로이드 / Kotlin] 진동
  • [안드로이드 / Kotlin] Retrofit2
  • [안드로이드 / Kotlin] 생체 인식 인증 사용하기
서주냥
서주냥
간단한 것도 기록하는 습관을 가지자
  • 서주냥
    DroidLog
    서주냥
  • 전체
    오늘
    어제
    • 전체보기 (58)
      • 알고리즘 (12)
        • 백준 (4)
        • 프로그래머스 (5)
        • 개념 (3)
      • Android (43)
        • Compose (1)
      • Java (2)
      • Kotlin (1)
  • 링크

    • GitHub
  • 인기 글

  • 태그

    ConstraintLayout
    Clean Architecture
    뷰모델
    SnackBar
    다이나믹 프로그래밍
    Coroutine
    이진 탐색
    코루틴
    최단 경로
    클린 아키텍처
    reified
    BLE
    textunit
    debounce
    벨만 포드
    프로그래머스
    viewpager2
    Coroutine Flow
    알고리즘
    자바
    코틀린
    백준
    viewmodel
    투 포인터
    moshi
    FusedLocationProviderClient
    RecyclerView
    블루투스
    안드로이드
    Hilt
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
서주냥
[안드로이드 / Kotlin] RecyclerView에 대한 고찰

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.