안드로이드 코틀린: recyclerview
recyclerview를 설명하기 전에 listview와 gridview를 설명하겠다.
이해를 돕기 위한 것도 있고 시기 상으로 이 둘이 먼저 먼저 나왔고 이 둘의 단점을 보완한 것이 recyclerview이다.
gridview와 listview는 리스트를 구현할 때 생성과 삭제를 반복하게 된다. 이들 하나하나가 객체라서 객체의 생성과 삭제를 반복하는 것은 성능 상 좋지 않기 때문에 recyclerview가 나왔다.
recyclerview 를 한글로 번역하면 재활용뷰라고 할 수 있다. 말 그대로 생성과 삭제를 하지 않고 썼던 것을 변경해서 다시 쓰는 것이다.
그림을 보면 이해가 빠를 것이다.
왼쪽이 gridview와 listview . 오른쪽이 recyclerview 의 작동흐름이다.
recyclerview 를 사용하기 위해서는 adapter 와 viewholder를 알고 있어야 한다.
adapter는 데이터를 recyclerview가 필요로 하는 요구사항에 맞춰서 전해주기 위한 객체이다.
콘센트를 생각하면 편할 것이다. 우리나라는 220v를 쓰지만 220v를 쓰지 않는 나라에서는 변환플러그를 사용해야 한다. adapter는 변환 플러그 같은 것이다.
viewholder는 빈 그릇이라고 생각하면 된다.
listview와 gridview는 사용하고 난 다음 그릇을 버리지만 recyclerview는 버리지 않고 재사용한다.
예제코드
MainActivity.kt
package com.example.testuse
import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.testuse.databinding.ActivityMainBinding
import com.example.testuse.databinding.ItemRecyclerviewBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding:ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityMainBinding.inflate(layoutInflater)
super.onCreate(savedInstanceState)
setContentView(binding.root)
// 데이터 원본 준비
val dataList = mutableListOf<MyItem>()
dataList.add(MyItem(R.drawable.a_123, "Bella", "1"))
dataList.add(MyItem(R.drawable.a_123, "Charlie", "2"))
dataList.add(MyItem(R.drawable.a_123, "Daisy", "1.5"))
dataList.add(MyItem(R.drawable.a_123, "Duke", "1"))
dataList.add(MyItem(R.drawable.a_123, "Max", "2"))
dataList.add(MyItem(R.drawable.a_123, "Happy", "4"))
dataList.add(MyItem(R.drawable.a_123, "Luna", "3"))
dataList.add(MyItem(R.drawable.a_123, "Bob", "2"))
dataList.add(MyItem(R.drawable.a_123, "Charlie", "2"))
dataList.add(MyItem(R.drawable.a_123, "Daisy", "1.5"))
dataList.add(MyItem(R.drawable.a_123, "Duke", "1"))
dataList.add(MyItem(R.drawable.a_123, "Max", "2"))
dataList.add(MyItem(R.drawable.a_123, "Happy", "4"))
dataList.add(MyItem(R.drawable.a_123, "Luna", "3"))
dataList.add(MyItem(R.drawable.a_123, "Bob", "2"))
dataList.add(MyItem(R.drawable.a_123, "Charlie", "2"))
dataList.add(MyItem(R.drawable.a_123, "Daisy", "1.5"))
dataList.add(MyItem(R.drawable.a_123, "Duke", "1"))
dataList.add(MyItem(R.drawable.a_123, "Max", "2"))
dataList.add(MyItem(R.drawable.a_123, "Happy", "4"))
dataList.add(MyItem(R.drawable.a_123, "Luna", "3"))
dataList.add(MyItem(R.drawable.a_123, "Bob", "2"))
dataList.add(MyItem(R.drawable.a_123, "Charlie", "2"))
dataList.add(MyItem(R.drawable.a_123, "Daisy", "1.5"))
dataList.add(MyItem(R.drawable.a_123, "Duke", "1"))
dataList.add(MyItem(R.drawable.a_123, "Max", "2"))
dataList.add(MyItem(R.drawable.a_123, "Happy", "4"))
dataList.add(MyItem(R.drawable.a_123, "Luna", "3"))
dataList.add(MyItem(R.drawable.a_123, "Bob", "2"))
dataList.add(MyItem(R.drawable.a_123, "Charlie", "2"))
dataList.add(MyItem(R.drawable.a_123, "Daisy", "1.5"))
dataList.add(MyItem(R.drawable.a_123, "Duke", "1"))
dataList.add(MyItem(R.drawable.a_123, "Max", "2"))
dataList.add(MyItem(R.drawable.a_123, "Happy", "4"))
dataList.add(MyItem(R.drawable.a_123, "Luna", "3"))
dataList.add(MyItem(R.drawable.a_123, "Bob", "2"))
dataList.add(MyItem(R.drawable.a_123, "Charlie", "2"))
dataList.add(MyItem(R.drawable.a_123, "Daisy", "1.5"))
dataList.add(MyItem(R.drawable.a_123, "Duke", "1"))
dataList.add(MyItem(R.drawable.a_123, "Max", "2"))
dataList.add(MyItem(R.drawable.a_123, "Happy", "4"))
dataList.add(MyItem(R.drawable.a_123, "Luna", "3"))
dataList.add(MyItem(R.drawable.a_123, "Bob", "2"))
binding.recyclerv.adapter = MyAdapter(dataList)
val adapter = MyAdapter(dataList)
binding.recyclerv.adapter = adapter
binding.recyclerv.layoutManager = LinearLayoutManager(this)
adapter.itemClick = object : MyAdapter.ItemClick {
override fun onClick(view: View, position: Int) { // 이 예제는 구현을 main에서 했다.
val name: String = dataList[position].aName
Toast.makeText(this@MainActivity," $name 선택!", Toast.LENGTH_SHORT).show()
}
}
}
}
data class MyItem(val aIcon:Int,val aName:String,val aAge:String)
class MyAdapter(val mItems: MutableList<MyItem>) : RecyclerView.Adapter<MyAdapter.Holder>() {
interface ItemClick {
fun onClick(view : View, position : Int)
}
var itemClick : ItemClick? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val binding = ItemRecyclerviewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return Holder(binding)
}
override fun onBindViewHolder(holder: Holder, position: Int) {
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) {
val iconImageView = binding.icon
val name = binding.name
val age = binding.age
}
}
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">
<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/recyclerv"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
item_recyclerview.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="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/icon"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:padding="8dp"
android:scaleType="centerCrop"
android:src="@drawable/a_123"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="2"
android:orientation="vertical">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="20sp"
android:padding="4dp"
android:hint="name"/>
<TextView
android:id="@+id/age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="20sp"
android:padding="4dp"
android:hint="age"/>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>