[Android] RecyclerView(1) - 기본 사용법

혜령·2022년 1월 9일
2

Android 공부하기

목록 보기
3/8

등장 배경

RecyclerView이 무엇인지 설명하기 전에 등장하게 된 배경을 알아보겠습니다.

RecyclerView 이전에는 스크롤되는 리스트를 표현하기 위해서 ListView를 사용했습니다. ListView를 이용하면 간단하고 빠르게 리스트를 만들 수 있습니다.

하지만 이런 ListView에는 몇가지 단점이 있었습니다.

  • 스크롤 시 버벅임 : ListView는 데이터의 아이템 만큼 뷰를 생성하게 됩니다. 이렇게 뷰를 만들고 findViewById() 메서드를 사용하는 것은 많은 비용이 들게 됩니다.
  • 기본 애니메이션의 지원이 없다 : ListView는 애니메이션에 대한 기본 지원이 제공되지 않으며 개발자가 직접 구현을 해야합니다.
  • 수직 스크롤만 지원 : ListView는 오로지 수직 스크롤만 가능합니다.

이런 문제점들을 해결하기 위해서 등장한 것이 RecyclerView입니다.

RecyclerView는 무엇일까요?

RecyclerView는 데이터 집합을 개별 아이템 단위로 구성하여 출력하는 ViewGroup이고, 한 화면에 표시되지 않는 많은 데이터를 스크롤 되는 리스트로 표시하는 역할을 합니다.

RecyclerView를 사용하는 가장 큰 이유는 재사용성이 좋기 때문입니다.

🔎 RecyclerView는 무엇을 재사용하는 것일까요?

ListView는 리스트를 스크롤하면 등장하는 데이터만큼 View를 생성합니다. 따라서 비효율적입니다. 스크롤이 되면 새롭게 나타나야 하는 View도 있지만, 더이상 나타날 필요가 없어지는 View도 있습니다. 그래서 RecyclerView는 바로 이 View를 재사용을 합니다.

View 객체 자체를 재사용하면서 View가 담고 있는 데이터만 바인딩합니다. 따라서 View의 생성과 삭제가 반복되는 것보다 성능과 비용 면에서 효과적으로 사용이 가능해진 것입니다.

이렇게 RecyclerView가 유연함을 가지도록 한 구조에 대해서 알아보겠습니다.

ViewHolder

ViewHolder는 화면에 표시될 아이템 뷰를 저장하는 객체입니다.

RecyclerView는 몇 개의 뷰의 객체만 생성해서 재사용합니다. 그러기 위해서는 이런 뷰 객체를 기억하고 있을 객체가 필요하게 됩니다. 그것이 바로 ViewHolder입니다.

ViewHolder가 데이터를 가지고 있고, 그 데이터가 화면에 나타납니다. 스크롤을 내릴 때 이미 만들어진 ViewHolder가 존재한다면 데이터만 바인딩시켜서 사용하게 됩니다.

ListView에서도 성능 개선을 위해서 개발자가 직접 ViewHolder패턴을 적용시킬 수 있습니다.

Adapter

RecyclerView는 다른 리스트를 표시하는 요소들과 마찬가지로 Adapter를 필요로 합니다.

Adapter는 리스트를 화면에 표시하기 위해서 아이템 단위로 View로 생성을 해서 RecyclerView에 바인딩 시키는 작업을 하는 객체입니다. 개발자가 직접 작성하여 RecyclerView에 연결시킵니다.

개발자는 RecyclerView.Adapter을 상속받아서 새로운 어댑터를 생성해야 하고, 다음 3개의 메서드를 오버라이드를 해야 합니다.

  • getItemCount: 전체 아이템 개수를 리턴합니다.
  • onCreateViewHolder: 인자로 받는 viewType 형태의 아이템 뷰를 위한 ViewHolder 객체를 생성하는 함수입니다. ViewHolder는 한번에 보여지는 리스트 목록 개수에 약간의 버퍼를 추가한 개수만큼 생성이 됩니다.
  • onBindViewHolder: 생성된 ViewHolder에 데이터를 바인딩하는 함수입니다. 인자로 ViewHolder와 position을 받아서 holder의 데이터를 변경시킵니다. 이 함수는 스크롤을 해서 데이터 바인딩이 필요한 만큼 호출됩니다.

Adapter 함수의 작동 순서는 getItemCount → onCreateViewHolder → onBindViewHolder 입니다.

LayoutManager

RecyclerView는 아이템 뷰를 수직, 수평, 격자(Grid) 형태의 레이아웃으로 배치할 수 있습니다. 이런 RecyclerView의 레이아웃을 관리하는 것이 LayoutManager입니다.

  • LinearLayoutManager : 수평 또는 수직 방향, 일렬(Linear)로 아이템 뷰 배치.
  • GridLayoutManager : 바둑판 모양의 격자(Grid) 형태로 아이템 뷰 배치.
  • StaggeredGridLayoutManager ****: 엇갈림(Staggered) 격자(Grid) 형태로 아이템 뷰 배치.

또한 더이상 화면에 표시되지 않는 아이템 뷰를 재활용하는 시점에 대한 정책도 결정합니다.

사용방법

  1. Activity에 RecyclerView 추가

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".ui.favorite.MyFragment">
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </LinearLayout>
  2. RecyclerView의 item View 레이아웃 추가

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_marginVertical="10dp"
        android:layout_marginHorizontal="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
      <ImageView
                android:id="@+id/imageView"
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:layout_margin="10dp"/>
    	<TextView
    						android:id="@+id/textView"
    		        android:layout_width="wrap_content"
    		        android:layout_height="wrap_content"
    		        android:textSize="32sp"/>
    </LinearLayout>
  3. RecyclerView Adapter, ViewHolder 구현

    class FavoriteRecyclerAdapter(val items: ArrayList<Favorite>): RecyclerView.Adapter<FavoriteRecyclerAdapter.ViewHolder>(){
    
        interface OnItemClickListener{
            fun OnItemClick(url:String)
        }
    
        var itemClickListener:OnItemClickListener?=null
    
        inner class ViewHolder(val binding: FavoriteRowBinding):RecyclerView.ViewHolder(binding.root){
            init {
                binding.root.setOnClickListener {
                    itemClickListener?.OnItemClick(items[adapterPosition].url)
                }
            }
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            val view = FavoriteRowBinding.inflate(LayoutInflater.from(parent.context), parent, false)
            return ViewHolder(view)
        }
    
        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            holder.binding.apply {
                textView.text = items[position].fName
                imageView.setImageResource(items[position].id1)
            }
        }
    
        override fun getItemCount(): Int {
            return items.size
        }
    }
  4. RecyclerView의 Adapter와 LayoutManager 지정

    adapter = FavoriteRecyclerAdapter(items)
    binding.recyclerView.apply {
          addItemDecoration(decoration)
          layoutManager = LinearLayoutManager(requireContext(),RecyclerView.VERTICAL, false)
          adapter = this@MyFragment.adapter
    }

RecyclerView에 Listener 적용

RecyclerView는 ListView와 다르게 clickListener가 내장되어 있지 않습니다. 따라서 직접 구현을 해줘야합니다.

Adapter 안에 OnItemClickListenr를 interface로 만들고, ViewHolder에서 연결시키는 방법으로 구현을 해보겠습니다.

  1. Adapter안에 interface로 clickListener 생성

    class FavoriteRecyclerAdapter(val items: ArrayList<Favorite>): RecyclerView.Adapter<FavoriteRecyclerAdapter.ViewHolder>(){
        interface OnItemClickListener{
            fun onItemClick(url:String)
        }
    
        var itemClickListener:OnItemClickListener?=null
    		...
    }
  2. ViewHolder에서 clickListener 연결

    inner class ViewHolder(val binding: FavoriteRowBinding):RecyclerView.ViewHolder(binding.root){
            init {
                binding.root.setOnClickListener {
                    itemClickListener?.onItemClick(items[adapterPosition].url)
                }
            }
        }
  3. Activity에서 Click이벤트 구현

    ```kotlin
    class MainActivity : AppCompatActivity() {
    		...
    		adapter.itemClickListener = object : FavoriteRecyclerAdapter.OnItemClickListener{
    				override fun onItemClick(url: String) {
    					  val intent = Intent(requireContext(), WebViewActivity::class.java)
    			      intent.putExtra("url", url)
    			      startActivity(intent)
    			  }
    		}
    }
    ```

    Reference
    https://recipes4dev.tistory.com/154

profile
안녕하세요

0개의 댓글