본문 바로가기

프로그래밍/안드로이드

[Android/Kotlin] RecyclerView 리사이클러 뷰

리사이클러 뷰(RecyclerView)는 스크롤할 수 있는 목록을 만들 때 사용하는 뷰로 앱을 만들면서 굉장히 많이 사용하게 된다.

이 글에서는 RecyclerView의 사용 방법을 적는다.

 

RecyclerView로 목록 만들기  |  Android 개발자  |  Android Developers

 

RecyclerView로 목록 만들기  |  Android 개발자  |  Android Developers

RecyclerView를 사용하여 동적 콘텐츠의 목록과 그리드를 표시합니다.

developer.android.com


◆ RecyclerView 생성

레이아웃

액티비티에는 간단하게 RecyclerView 하나만 match_parent로 추가했다. 리사이클러 뷰의 id는 rv_list로 정했다.

activity_main.xml
 
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

추가로 리사이클러 뷰의 아이템으로 들어갈 레이아웃을 만들어야 한다. 적당히 원하는 모양으로 만들면 된다.

이때 내용이 변할 View들은 id를 정해줘야 하고, 최상위 레이아웃의 높이는 match_parent에서 다른 값으로 바꾸자.

 

여기서는 CardView 안에 TextView 두 개를 넣고 id를 각각 'tv_name', 'tv_number'로 정했다.

list_item.xml
 
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_margin="8dp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cardElevation="8dp"
    app:cardCornerRadius="8dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginTop="8dp"
            android:text="TextView"
            android:textColor="?android:attr/textColorPrimary"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/tv_number"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:layout_marginBottom="8dp"
            android:text="TextView"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="@+id/tv_name"
            app:layout_constraintTop_toBottomOf="@+id/tv_name" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

 

아이템 레이아웃 예시

아이템, 어댑터 클래스 생성

레이아웃을 만들었으면 아이템에 들어갈 내용을 담을 아이템 클래스와, 아이템들을 받아서 리사이클러 뷰에 추가할 어댑터 클래스를 생성해야 한다.

 

아이템 클래스는 레이아웃에서 만들었던 View의 개수대로 인자를 만든다.

ListItem.kt
 
class ListItem(val name: String, val number: String)

 

다음은 어댑터 클래스인데 아래와 같다.

ListAdapter.kt
 
class ListAdapter(val itemList: ArrayList<ListItem>): RecyclerView.Adapter<ListAdapter.ViewHolder>() {
    // (1) 아이템 레이아웃과 결합
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListAdapter.ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
        return ViewHolder(view)
    }
    // (2) 리스트 내 아이템 개수
    override fun getItemCount(): Int {
        return itemList.size
    }
    // (3) View에 내용 입력
    override fun onBindViewHolder(holder: ListAdapter.ViewHolder, position: Int) {
        holder.name.text = itemList[position].name
        holder.number.text = itemList[position].number
    }
    // (4) 레이아웃 내 View 연결
    class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
        val name: TextView = itemView.findViewById(R.id.tv_name)
        val number: TextView = itemView.findViewById(R.id.tv_number)
    }
}

 

RecyclerView 추가

위 과정을 모두 마쳤으면 액티비티에 리사이클러 뷰를 추가할 차례다. 참고로 본인은 ViewBinding을 사용 중이다.

MainActivity.kt
 
class MainActivity : AppCompatActivity() {
    private lateinit var binding : ActivityMainBinding
    val itemList = arrayListOf<ListItem>()      // 아이템 배열
    val listAdapter = ListAdapter(itemList)     // 어댑터

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 레이아웃 매니저와 어댑터 설정
        binding.rvList.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
        binding.rvList.adapter = listAdapter

        // 아이템 추가
        itemList.add(ListItem("Ada", "010-1234-5678"))
        itemList.add(ListItem("James", "010-1234-5678"))
        itemList.add(ListItem("John", "010-1234-5678"))
        itemList.add(ListItem("Cena", "010-1234-5678"))
        // 리스트가 변경됨을 어댑터에 알림
        listAdapter.notifyDataSetChanged()
    }
}

레이아웃 매니저는 리사이클러 뷰의 아이템을 어떻게 배치할지 설정하는 것인데 여기서는 세로 방향으로 일렬로 배치한다. 가로방향으로 배치하려면 VERTICAL을 HORIZONTAL로 바꾸면 된다.

만약 격자무늬로 배치하고 싶으면 LinearLayoutManager 대신 GridLayoutManager를 사용한다.

 

아이템이 추가되거나 삭제되면 notifyDataSetChanged()를 호출해 변경을 반영해야 한다.

실행하면 다음과 같은 화면이 나온다.