Tech Log

[Android] RecyclerView 본문

Android/Widget

[Android] RecyclerView

yuhee kim 2022. 7. 21. 22:34

RecyclerView는 안드로이드 위젯 중에서 많이 쓰이는 것 중 하나다. RecyclerView는 메모리 최적화와도 관련된 내용이 나오기 때문에, 이론적인 내용에 대한 정리가 필요할 것 같아 글을 작성해본다.

 

 

1. RecyclerView란?

Android Developers에서는 RecyclerView를 동적 목록을 나타내는 수단으로 표현하고 있다.

이름에서 알 수 있듯이, RecyclerView내의 View를 재활용하는 동적 목록이다.

 

Android Jetpack에 포함되어 있는 위젯이다(원래는 Support Library에서 지원됐으나, Support Library가 deprecated됨)

 

다시 말해, 데이터 목록을 재활용하여 목록에 나타낸 View라고 생각하면 된다.

 

아래와 같은 아이템 목록들을 나타내는데 쓰인다.

ListView라는 클래스도 아이템 목록을 나타내는데 쓰이는데,

RecyclerView와는 메모리 사용에 대해서 차이점이 있다.

위에서 언급했듯이, RecyclerView는 데이터 목록을 재활용하기 때문이다.

 

출처 :https://howtodoandroid.com/recyclerview-android/

 

2. ListView와의 차이

ListView와 RecyclerView 모두 아이템 목록을 나타내는 위젯이다.

이때 아이템은 단순히 Text만 출력하는 구조가 될 수도 있고,

Image, Button, CheckBox 등 여러 View의 조합으로 구성될 수도 있다.

 

ListView와 RecyclerView의 가장 큰 차이를 나타낸 사진이 다음과 같다.

출처 : https://dev.to/jbc7ag/recyclerview-or-listview-pros-cons-and-examples-with-kotlin-2nb2

 

사진과 같이 ListView는 View를 재활용하지 않는다.

그렇기 때문에 ListView의 아이템은 리스트 항목이 갱신될 때마다 매번 새로 구성돼야 한다.

즉, 스크롤을 내렸다가 올리는 것과 같이 스크롤을 왔다갔다 할 때 마다, 리소스를 새로 불러오게 된다.

 

이는 아이템의 양이 많아질수록, 낮은 성능을 보일 수 있다.

여기서 말하는 낮은 성능은, 많은 메모리를 사용하게 되어 앱이 느려진다는 것이다.

 

이러한 ListView의 큰 단점을 해결하는 것이 RecyclerView다.

 

사진에 보이듯이 View를 Reuse한다.

RecyclerView는 이미 보았던 View가 사라지고 스크롤을 다시 올렸을 때 이전에 보았던 View를 재사용하는 것이다.

반면, ListView는 이미 보았던 View라도 스크롤을 다시 올리면 해당 View를 재사용하지 않고 새로 만든다.

 

ListView와 RecyclerView의 차이점은 위에서 설명한 것 이외에도 존재한다.

해당 차이점을 표로 정리한 것이 다음과 같다.

 

  ListView RecyclerView
ViewHolder
(화면에 표시될 아이템 View을 저장하는 객체)
필요 유무
필요로 하지 않음 필요
Item Layout 설정 가능 범위 세로 방향만 가능
(다른 방향 가능하나, 상당 부분 재구현 필요)
가로 / 세로/ 격자 모두 가능
Item Animation 가능 여부 ListView 내에 아이템 View의 animation을 처리할 수 있는 클래스가 없다. 아이템 View가 추가 / 삭제될 때마다 animation 처리가 가능한 클래스가 있다.
(RecyclerView.ItemAnimator)
Item Decoration 처리 방법 xml 속성의 android:divider 사용하여 아이템 마다 구분선 적용 RecyclerView.ItemDecoration 사용하여 아이템의 decoration 적용
Adapter 유무 배열, DB 내용을 View로 바꾸어주는 Adapter가 여러 개 존재. ArrayAdapter, CursorAdapter 등 여러 Adapter가 있다. 데이터 종류에 따른 Adapter가 여러 개 있는 것 아니라, RecyclerView.adapter에서 데이터에 맞는 Adapter를 커스텀해서 사용
Click Event 처리 방법 AdapterView.OnItemClickListener 사용하여 아이템에 binding하여 Click Event 적용 RecyclerView.OnItemTouchListener 사용하여 아이템 개별적으로 Click Event 적용

 

3. RecyclerView의 구성

출처 : https://medium.com/@simple.schwarz/how-to-make-a-list-app-with-recyclerview-1-android-kotlin-example-e5feca8b70fe

 

3.1 RecyclerView

데이터에 맞게 여러 개의 View를 담는 Container.

다시 말해, 데이터 목록을 나타낸 View 그 자체를 말한다.

RecycleView 객체는 화면에 보이지 않는(ViewHolder에 없는) 아이템 View도 참조(reference, binding)한다.

나중에 다시 불러와서 재사용하기 위해서이다(Caching)

 

3.2 Adapter

RecyclerView에 나타낼 데이터를 아이템 View로 만들어주는 역할을 한다.

 

3.3 LayoutManager

RecyclerView 내의 아이템 View를 어떻게 배치할지를 관리한다.

LayoutManager가 아이템 View를 배치하는 방법은 기본적으로 다음과 같다.

  • LinearLayoutManager : Horizontal, Vertically, Linear 방향으로 아이템 View를 배치한다.
  • GridLayoutManager : Grid(격자) 형태로 아이템 View를 배치한다.
  • StaggeredGridLayoutManager : Staggered Grid(엇갈린 격자) 형태로 아이템 View를 배치한다.

출처 : https://tutorial.eyehunts.com/android/recyclerview-android-example-cardview-kotlin/

 

위와 같은 배치 형태는 안드로이드 SDK에서 기본적으로 제공되는 형태다.

이외에도 개발자가 다양한 형태의 LayoutManager를 커스텀할 수 있다.

 

LayoutManager는 화면에 보이지 않는(ViewHolder에 없는) 아이템 View가 언제 재활용(Reuse)되는지도 관리한다.

 

3.4 ViewHolder

RecyclerView 내의 아이템 View 중 화면에 표시될 아이템 View를 저장하는 객체.

Adapter에 의해 관리되며 생성된다.

아이템 View는 ViewHolder에 binding된다.

 

4. RecyclerView 예제

4.1 Activity(혹은 Fragment)에 RecyclerView 추가

    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/recyclerview"/>

 

4.2 아이템 View의 Layout 작성(LayoutManager가 아니라 xml 파일 작성)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    
    <TextView
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="item"
        android:id="@+id/text"/>

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="50dp"
        android:layout_height="50dp"
        app:srcCompat="@drawable/ic_launcher_foreground" />
    
</LinearLayout>

 

4.3 RecyclerView Adapter, ViewHolder 구현

Adapter 구현 시, 다음과 같은 메소드를 override 해줘야 함

  • onCreateViewHolder() : ViewHolder 생성하고 생성한 ViewHolder를 초기화. 아이템 View가 binding 되어 있는 단계가 아니므로, 아이템 View의 내용(데이터, 콘텐츠)를 작성하지 않는다.
  • onBindViewHolder() : position parameter에 해당하는 데이터를 ViewHolder 내의 아이템 View에 나타낼 수 있도록 한다.
  • getItemCount() : RecyclerView 내의 아이템 View 개수를 return. 화면에 보여줄 아이템 View가 없을 때 해당 메소드를 이용하여 코드를 작성한다.
class RVAdapter(private val dataSet: Array<String>) : RecyclerView.Adapter<RVAdapter.ViewHolder>() {

    //custom ViewHolder
    class ViewHolder(view : View) : RecyclerView.ViewHolder(view) {
        val textView : TextView

        init {
            textView = view.findViewById(R.id.text)
        }

    }

    //LayoutManager에 의해 새로운 아이템 View 생성
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.recyclerview_item, parent, false)

        return ViewHolder(view)
    }

    //LayoutManager에 의해 아이템 View의 내용을 replace
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {

        //아이템 View의 내용을 사용자 데이터를 불러와서 position에 맞게 대체
        holder.textView.text = dataSet[position]
    }

    //LayoutManager에 의해 사용자 데이터의 크기(size)를 return
    override fun getItemCount() = dataSet.size


}

 

4.4 LayoutManager 객체 생성

val layoutmanager = LinearLayoutManager(this)

Grid, StaggeredGrid, Linear 중에서 Linear 형태로 LayoutManager를 만들어주었다.

LayoutManager의 parameter는 context가 들어간다.

 

*context : 애플리케이션의 현재 상태 맥락(context)를 말한다. 애플리케이션 환경 정보를 담고 있다.

context에 대한 내용은 따로 정리를 해봐야할 것 같다. context를 잘 사용해야 메모리 누수를 방지할 수 있다. 그렇기 때문에 context도 꽤 중요한 내용이 될 것 같다.

 

4.5 RecyclerView Adapter 객체 생성, 4.1의 RecyclerView와 Adapter 연결

class MainActivity : AppCompatActivity() {

    lateinit var binding : ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val dogs = arrayOf("슈나우저", "볼로네즈", "비글", "비숑", "사모예드") //사용자 데이터

        val myRecyclerView = findViewById<RecyclerView>(R.id.recyclerview)

        val myAdapter = RVAdapter(dogs)
        val layoutmanager = LinearLayoutManager(this)

        myRecyclerView.adapter = myAdapter
        myRecyclerView.layoutManager = layoutmanager

        //RecyclerView의 size를 고정
        myRecyclerView.setHasFixedSize(true)


    }
}

 

결과

많이 작지만, 목록이 더 길면 스크롤이 가능해진다.

스크롤이 가능해지면 화면에 나오는 것만 ViewHolder에 있는 것이고 화면에 안나오는 것은 ViewHolder 밖에 있는 것이다.

 

 

ViewPager 만큼이나 복잡한 생성인 것 같다.

여기서 RecyclerView를 더 잘 활용하려고 하고, Custom하려고 하면 더 복잡해질 수도 있겠다.

그래도 계속 쓰면 나름 익숙해질 것 같기도 하다.

메모리 관리(OOM, Out Of Memory 해결)와 관련이 있기 때문에, 그만큼 잘 활용해야 하는 위젯인 것 같다.

 

 

참조

 

'Android > Widget' 카테고리의 다른 글

[Android] Android Architecture Components(AAC)  (0) 2022.12.21
[Android] ViewPager2  (0) 2022.06.23
Comments