안드로이드/Deep in the AOS

RecyclerView :: Recycle Love you❤️ [ Deep in the AOS ]

RecyclerView를 사용하면 대량의 데이터 세트를 효율적으로 표시할 수 있습니다.

개발자가 데이터를 제공하고 각 항목의 모양을 정의하면 RecyclerView 라이브러리가 필요할 때 요소를 동적으로 생성합니다.

 

이름에서 알 수 있듯 Recycler View는 이러한 개별 요소를 재활용합니다.

항목이 스크롤되어 화면에서 벗어나도 RecyclerView는 View를 제거하지 않습니다.

대신 RecyclerView는 화면에서 스크롤된 새 항목의 뷰를 재사용합니다.

이렇게 뷰를 재사용하면 앱의 응답성을 개선하고 전력 소모를 줄이기 때문에 성능이 개선됩니다.

 

RecyclerView에서 레이아웃 계획

RecyclerView의 레이아웃은 LayoutManager 클래스를 통해 결정됩니다.

RecyclerView 라이브러리는 가장 일반적인 레이아웃 상황을 처리하는 3가지로 제공합니다.

1. LinearLayoutManager - 항목을 1차원 목록으로 정리

2. GridLayoutManager - 항목을 2차원 그리드로 정리

3. StaggeredGridManager - Grid와 비슷하나 행의 항목이 동일한 높이거나 동일한 너비일 필요가 없으며 행 또는 열의 항목이 서로 오프셋 상태가 될 수 있음

 

RecyclerView의 어댑터 및 뷰 홀더

Adapter(어댑터)는 데이터를 올리는 부분에서 View와 Data를 연결해주는 다리역할을 합니다.

데이터의 원본을 받아서 관리하고 어댑터뷰에 출력할 수 있는 형태로 데이터를 다시 제공하는 객체입니다.

 

Adapter View는 어댑터가 제공한 객체를 출력하는 역할을 합니다.

많은 정보를 효율적으로 처리하기 위해 View에 바로 데이터를 제공하지 않고 어댑터라는 객체를 이용하기에 정해진 이름입니다.

 

View Holder는 무엇일까요? 🧐

맨 처음 화면에 보이는 View 객체를 Holding해야 다시 재사용 할 수 있기 때문에 ViewHolder를 사용합니다.

ListView는 사용자가 스크롤 할 때마다 위에 있던 View는 삭제되고, 맨 아래의 뷰는 생성되길 반복해 비용이 많아집니다.

그런데 RecyclerView는 스크롤을 계속 해도 화면에 보이는 정도의 View만 생성하고 스크롤 할 때마다 삭제하지 않고 가장 아래의 아이템쪽으로 객체를 이동시켜 재사용합니다.

이를 위해 View Holder를 사용합니다.

그런데 이 View Holder는 왜 생긴거에요? 😕

Recycler View를 사용하기 전 우리는 ListView를 사용했습니다.

데이터 각각에 대해 아이템의 레이아웃을 구성하는 View를 inflate 하고, Inflating 된 뷰에서 findViewById()를 통해 데이터를 맞춰주며 리스트 형태로 만들어줬습니다.

이를 최적화 하기 위해서 contentView를 도입해 재사용성을 높였지만 역부족이었습니다.

findViewById() 호출도 매우 고비용 작업이라 데이터의 개수가 늘어나는 만큼 성능이 저하됩니다.

🥺 findViewById가 왜 고비용 작업이에요..?

그냥 TextView, ImageView 등 단일 뷰 자체에 대한 findViewById는 비용이 적습니다.

그런데 여러 개 Child View를 포함하는 Layout의 경우엔 말이 달라집니다.

자신과 자신의 자식을 모두 확인해야합니다. (마치 DFS 탐색 같이...)

그렇기에 매번 Child View를 모두 확인해 가져오는 과정은 크게 비용이 발생합니다.

 

이런 문제로 인해 View Holder가 등장합니다.

각 뷰의 객체를 ViewHolder에 넣었다가 필요할 때 꺼내쓰는 개념입니다

 

ViewHolder는 어떻게 구현할까요?

RecyclerView.Adapater에서 뷰 홀더를 구현할 때 세 가지 키 메서드를 재정의해야 합니다.

onCreateViewHolder(): ViewHolder를 새로 생성할 때마다 이 메서드를 호출합니다. 뷰 객체를 생성해 뷰 홀더 객체에 담아 리턴함

 

onBindViewHolder(): ViewHolder를 데이터와 연결할 때 이 메서드를 호출 뷰 홀더 객체들의 레이아웃을 채워줌 (position 파라미터 활용해 데이터의 순서에 맞게 아이템 레이아웃을 바인딩할 수 있음)

 

getItemCount():  데이터 세트 크기를 가져올 때 이 메서드를 호출

 

저는 Recycler View를 구현할 때 다음과 같이 구현합니다.

data class Item (
    val name: String,
    val src: String
)
class CustomRecyclerAdapter(private val context: Context, private var itemList: ArrayList<>): RecyclerView.Adapter<CustomRecyclerAdapter.ListInfoHolder>() {

    lateinit var onItemClickListener: OnItemClickListener

    interface OnItemClickListener {
        fun onClick(view: View, position: Int)
    }

    fun setItemClickListener(listener: OnItemClickListener) {
        this.onItemClickListener = listener
    }

    inner class ListInfoHolder(private val view: View): RecyclerView.ViewHolder(view) {
        fun bindInfo(item: Item) {
            Log.d(TAG, "bindInfo: ${item}")
            view.findViewById<TextView>(R.id.listTvTitle).text = item.name
            Glide.with(view)
                .load(item.src)
                .into(view.findViewById<ImageView>(R.id.listIvImage))
        }
    }
    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): CustomRecyclerAdapter.ListInfoHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)

        return ListInfoHolder(view)
    }

    override fun onBindViewHolder(holder: CustomRecyclerAdapter.ListInfoHolder, position: Int) {
        holder.apply {
            bindInfo(itemList[position])
            itemView.setOnClickListener {
                onItemClickListener.onClick(it, position)
            }

        }
    }

    override fun getItemCount(): Int {
        return itemList.size
    }
}



onCreateViewHolder()를 통해 화면에 보이는 리스트 목록에서 +a 정도의 뷰 객체를 담은 ViewHolder를 생성합니다.

onBindViewHolder(): ViewHolder를 데이터와 연결할 때 이 메서드를 호출 뷰 홀더 객체들의 레이아웃을 채워줍니다.

뷰 홀더 객체인 ListInfoHolder의 fun bindInfo()를 활용하여 데이터를 바인딩할 수 있습니다.

 

RecyclerView를 그냥 막 쓰는 것 보다 어느 정도 알고 쓰는 것이 좋다고 생각하여 이번 포스팅을 작성해봤습니다 !

RecyclerView가 왜 좋은 지, 왜 사용해야 하는 지 그리고 View Holder가 어떤 개념인 지 알 수 있는 기회가 되었던 것 같습니다.