실무에서 가장 많이 쓰는 AdapterView는 RecyclerView! 반드시 완벽하게 숙지하자!
RecyclerView는 안드로이드 앱에서 리스트 형태의 데이터를 표시하는데 사용되는 위젯이다. 여러 아이템을 스크롤 가능한 리스트로 표현하며(가로 스크롤도 가능), 많은 아이템을 효율적으로 관리하고 보여주는 역할을 한다.
1) ListView
2) RecyclerView
LayoutManager
는 RecyclerView
내부의 아이템들이 어떻게 배치될지를 결정한다.LayoutManager
로는 LinearLayoutManager
, GridLayoutManager
, StaggeredGridLayoutManager
등이 있다.recyclerView.layoutManager = LinearLayoutManager(this) // 수직 리스트를 위한 LinearLayoutManager
// 또는
recyclerView.layoutManager = GridLayoutManager(this, 3) // 3열 그리드를 위한 GridLayoutManager
RecyclerView.Adapter
RecyclerView는 화면에 보여주는 역할을 하는 뷰이고, 거기에 들어갈 데이터가 필요하다. 뷰와 데이터를 연결해 주어야 한다. 이 연결해 주는 역할을 하는 것이 어댑터이다. RecyclerView 안에 Adapter라는 게 이미 추상화되어서 선언돼있다. 이것을 상속해서 구현하면 된다.
RecyclerView
에 표시될 데이터와 해당 데이터를 보여줄 ViewHolder
를 연결한다.Adapter
는 데이터셋의 변경 사항을 RecyclerView
에 알리고, 데이터를 기반으로 뷰를 생성한다.ViewHolder
RecyclerView
의 개별 아이템 뷰를 위한 객체이다.inner class Holder(val binding: ItemRecyclerviewBinding) : RecyclerView.ViewHolder(binding.root) { // holder를 만들고 계속해서 재활용
val iconImageView = binding.iconItem
val name = binding.textItem1
val age = binding.textItem2
}
1) 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>
2) data Class 만들기. (MyItem)
data class MyItem(val aIcon:Int, val aName:String, val aAge:String)
3) 어댑터 클래스 정의
앞서 정의한 MyItem타입의 객체들을 ArrayList로 관리하는 MyAdapter클래스를 RecyclerView.Adapter를 파생하여 정의한다.
// MyAdapter.kt
class MyAdapter(val mItems: MutableList<MyItem>) : RecyclerView.Adapter<MyAdapter.Holder>() { // 인자로 리스트를 넣어주고, RecyclerView의 Adapter를 상속받음. 상속받으면 ItemId, ItemCount를 꼭 오버라이딩해야 한다.
interface ItemClick {
fun onClick(view : View, position : Int)
}
var itemClick : ItemClick? = null // itemClick은 타입이 ItemClick
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val binding = ItemRecyclerviewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return Holder(binding) // binding을 Holder에 넣어줌
}
override fun onBindViewHolder(holder: Holder, position: Int) { // onBindViewHolder가 8번 돌면서 mItems에 있는 8개를 하나하나 실행시킴(화면에 보이는데까지만 실행됨. 밑으로 스크롤해서 보여질 때 onBindViewHolder가 불림) Holder에 차례대로 들어옴. position은 0번째 부터 들어옴
holder.itemView.setOnClickListener { //클릭 이벤트 추가 부분
itemClick?.onClick(it, position)
}
holder.iconImageView.setImageResource(mItems[position].aIcon)
holder.name.text = mItems[position].aName
holder.age.text = mItems[position].aAge
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemCount(): Int {
return mItems.size
}
inner class Holder(val binding: ItemRecyclerviewBinding) : RecyclerView.ViewHolder(binding.root) { // holder를 만들고 계속해서 재활용
val iconImageView = binding.iconItem
val name = binding.textItem1
val age = binding.textItem2
}
}
onCreateViewHolder
는 ViewHolder 객체를 생성하는 메서드이다. 이 메서드는 RecyclerView.Adapter에 정의되어 있으며, 각 아이템을 위한 View를 생성하고 그 View를 감싸는 ViewHolder 객체를 반환한다.parent: ViewGroup
: 이 매개변수는 ViewHolder가 결국에 추가될 ViewGroup을 의미한다. 대부분의 경우, 이것은 RecyclerView 자체이다.viewType: Int
: 이 매개변수는 다양한 뷰 타입을 구별하는 데 사용된다. 하나의 RecyclerView에서 다양한 레이아웃을 보여줘야 할 때 유용하다.LayoutInflater.from(parent.context).inflate(R.layout.item_view, parent, false)
: 이 코드 라인은 XML 레이아웃 파일(item_view.xml
)을 실제 View 객체로 변환한다. LayoutInflater
는 XML을 실제 View 객체로 인플레이션하는데 사용된다.parent.context
: 현재의 Context를 가져오는 방법.inflate
메서드는 세 가지 매개변수를 받는다:false
로 설정되어 있어 부모 뷰그룹에 바로 추가되지 않고, 나중에 RecyclerView에 의해 추가된다.MyViewHolder(itemView)
: 여기서 MyViewHolder
는 RecyclerView.ViewHolder
를 상속받은 사용자 정의 클래스이다. 이 클래스는 레이아웃 내의 뷰들을 캐싱하고 재사용한다.return MyViewHolder(itemView)
: 생성된 MyViewHolder
인스턴스를 반환한다. RecyclerView는 이 ViewHolder를 사용하여 각 아이템의 데이터를 바인딩하고 화면에 표시한다.4) MainActivity
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>() // data Class를 만들고(MyItem), dataList에 샘플 데이터들을 추가한다. 지금은 샘플 데이터를 쓰지만, 실제 프로젝트 진행할 떄는 네트워크를 통해 서버에서 데이터를 받아올 것이다.
dataList.add(MyItem(R.drawable.sample_0, "Chopa", "9"))
dataList.add(MyItem(R.drawable.sample_1, "Chopa", "9"))
dataList.add(MyItem(R.drawable.sample_2, "Chopa", "9"))
dataList.add(MyItem(R.drawable.sample_3, "Duckman", "8.5"))
dataList.add(MyItem(R.drawable.sample_4, "Duckman", "8.5"))
dataList.add(MyItem(R.drawable.sample_5, "Duckman", "8.5"))
dataList.add(MyItem(R.drawable.sample_6, "Ddakji", "5"))
dataList.add(MyItem(R.drawable.sample_7, "Ddakji", "5"))
val adapter = MyAdapter(dataList) // adapter는 MyAdapter이고, 그 안에 실제로 보여줄 dataList를 넣음.
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = LinearLayoutManager(this) // layoutManager는 레이아웃을 어떻게 구성할건지.
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()
}
}
}
}