코틀린(Kotlin)/TIL

[코틀린(Kotlin)] 리사이클러 뷰 (RecyclerView)

초보왕보초 2024. 1. 5. 20:01
728x90

리사이클러 뷰 (RecyclerView)

안드로이드 앱에서 리스트 형태의 데이터를 표시하는 데 사용되는 위젯

여러 아이템을 스크롤 가능한 리스트로 표현하며, 많은 아이템을 효율적으로 관리하고 보여주는 역할을 한다

  • 한정적인 화면에 많은 데이터를 넣을 수 있는 View
  • View를 재활용(Recycle) 해서 사용

 

 

리스트 뷰(ListView)와 리사이클러 뷰(RecyclerView)

  • 리스트 뷰 (ListView)
    - 사용자가 스크롤할 때마다 위에 있던 아이템은 삭제되고, 맨 아래의 아이템은 생성되길 반복한다
    - 아이템이 100개면 100이 삭제, 생성을 반복하므로 성능에 좋지 않다

 

  • 리사이클러 뷰 (RecyclerView)

    - 사용자가 스크롤할 때, 위에 있던 아이템은 재활용돼서 아래로 이동하여 재사용한다
    - 아이템이 100개여도 10개 정도의 View만 만들고 10개를 재활용하여 사용한다
    - View를 계속 만드는 리스트 뷰의 단점을 보완하기 위해 나왔다

 

 

 

리사이클러 뷰(RecyclerView) 사용에 필요한 것

1) Adapter

- 데이터 테이블을 목록 형태로 보여주기 위해 사용되는 것으로, 데이터를 다양한 리스트 형식으로 보여주기 위해 데이터와 리사이클 뷰 사이에 존재하는 객체

[어댑터 뷰 편 참고]

 

2) ViewHolder

- 화면에 표시될 데이터나 아이템들을 저장하는 역할

- 리사이클러 뷰의 개념을 적용하기 위해선 스크롤해서 위로 올라간 View를 재활용하기 위해 이 View를 기억하고 있어야 하는데, ViewHolder가 그 역할을 한다

 

 

리사이클러 뷰(RecyclerView) 사용하기

 

예시)

 

 

리사이클러 뷰를 사용하기 전, 뷰 바인딩을 추가해 준다

 

activity_main.xml

더보기
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

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

 

 

item_recyclerview.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="wrap_content"
    android:orientation="horizontal">
    <ImageView
        android:id="@+id/iconItem"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_gravity="center_vertical"
        android:layout_weight="1"
        android:padding="8dp"
        android:scaleType="centerCrop"
        android:src="@drawable/sample_1" />
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:orientation="vertical">
        <TextView
            android:id="@+id/textItem1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="Name"
            android:padding="4dp"
            android:textColor="#B30B0B"
            android:textSize="20dp" />
        <TextView
            android:id="@+id/textItem2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="Num"
            android:padding="4dp"
            android:textColor="#00FF37"
            android:textSize="16dp" />
    </LinearLayout>
</LinearLayout>

 

MyItem.kt (데이터 클래스)

더보기
package com.android.ex_view

data class MyItem(val aIcon:Int, val aName:String, val aNum:String){

}

 

MainActivity.kt

더보기
package com.android.ex_view

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.ArrayAdapter
import android.widget.LinearLayout
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import com.android.ex_view.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 데이터 원본 준비
        val dataList = mutableListOf<MyItem>()
        dataList.add(MyItem(R.drawable.sample_1, "add","1"))
        dataList.add(MyItem(R.drawable.sample_2, "search","2"))
        dataList.add(MyItem(R.drawable.sample_3, "home","3"))
        dataList.add(MyItem(R.drawable.sample_4, "menu","4"))
        dataList.add(MyItem(R.drawable.sample_5, "cancel","5"))
        dataList.add(MyItem(R.drawable.sample_6, "option","6"))
        dataList.add(MyItem(R.drawable.sample_7, "check","7"))
        dataList.add(MyItem(R.drawable.sample_8, "plus","8"))
        dataList.add(MyItem(R.drawable.sample_9, "delete","9"))

        // 어뎁터 생성 후 데이터리스트 넣어줌
        val adapter = MyAdapter(dataList)

        // 만든 어댑터를 recyclerView 어댑터에 넣어줌
        binding.recyclerView.adapter = adapter
        // layourManager = 레이아웃 어떻게 구성할건지
        binding.recyclerView.layoutManager = LinearLayoutManager(this)

        // MyAdapter.kt 36라인에서 받은 클릭이벤트 처리
        adapter.itemClick = object : MyAdapter.ItemClick{
            override fun onClick(view: View, position: Int) {
                val name: String = dataList[position].aName
                Toast.makeText(this@MainActivity, "$name 선택!",Toast.LENGTH_SHORT).show()
            }
        }
    }
}

 

MyAdapter.kt

더보기
package com.android.ex_view

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.android.ex_view.databinding.ItemRecyclerviewBinding

// MyAdapter를 Adapter로 사용하기 위해선 RecyclerView.Adapter를 상속받아야 한다
// <>안에는 뷰홀더를 넣어줘야 함. inner class로 만든 MyAdapter.Holder를 넣어준다
class MyAdapter(val mItems: MutableList<MyItem>) : RecyclerView.Adapter<MyAdapter.Holder>() {

    // 클릭 이벤트 추가 부분
    interface ItemClick {
        fun onClick(view : View, position : Int)
    }

    // 클릭 이벤트 추가 부분
    var itemClick : ItemClick? = null

    // viewHolder가 생성되는 함수, 여기서 viewHolder 객체를 생성한다
    // 여기서 반환한 뷰 홀더 객체는 자동으로 onBindViewHolder() 함수의 매개변수로 전달된다
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        // 바인딩 선언 후 .inflate() 부분은 공식처럼 생각하면 됨(안바뀜)
        val binding = ItemRecyclerviewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return Holder(binding)
    }

    // 중요 // 화면에 보여질 때 실행
    // 매개변수로 전달된 뷰 홀더 객체의 뷰에 데이터를 출력하거나 필요한 이벤트를 등록한다
    // 매개변수로 있는 position은 아이템 중 지금 아이템이 몇번째 아이템인지 알려준다
    override fun onBindViewHolder(holder: Holder, position: Int) {
        // 인터페이스 // 클릭 이벤트 추가 부분(여기서 해도 됨) / 지금 이 코드에선 클릭이벤트 받아서 메인액티비티에 보내줌 -> 메인이벤트에서 처리
        // 메인액티비티로 보내주려면 메인액티비티랑 어뎁터 사이에 통신가능한 인터페이스를 생성해줘야 함 = 15라인
        holder.itemView.setOnClickListener {
            itemClick?.onClick(it, position)
        }
        holder.iconImageView.setImageResource(mItems[position].aIcon)
        holder.name.text = mItems[position].aName
        holder.num.text = mItems[position].aNum
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }
    // RecyclerView에 몇가지의 아이템이 떠야되는지 알려주는 메서드
    override fun getItemCount(): Int {
        return mItems.size
    }

    // ItemRecylerviewBinding = item_recyclerview.xml 뷰의 홀더, Holder가 계속해서 재활용 됨
    inner class Holder(val binding: ItemRecyclerviewBinding) : RecyclerView.ViewHolder(binding.root) {
        val iconImageView = binding.iconItem
        val name = binding.textItem1
        val num = binding.textItem2
    }
}

 

 

RecyclerView에 adapter를 연결해 줘야 하는데 그 adapter를 만들어 준 것이다

  • MyAdapter 생성자는 <MyItem>타입의 MutableList를 받는다고 선언한다
    - MyItem은 위에서 만들었던 데이터 클래스
  • RecyclerView.Adapter를 상속받은 class MyAdapter()는 필수 구현을 넣어줘야 한다
    - ovrride fun onCreateViewHolder / onBindViewHolder / getItemCount
    - inner class 홀더명
  • inner class로 Holder 클래스를 만들고 생성자로는 viewBinding으로 받아올 binding을 넣는다
    - 이 클래스는 뷰홀더로 사용할거니까 RecyclerView.ViewHolder를 상속받아야 한다
    - 생성자로는 바로 이전에 만든 binding의 root를 넣는다

 

 

 

 

 

 

 

 

 

 

728x90