RecyclerView를 사용하면 대량의 데이터 세트를 효율적으로 표시할 수 있습니다. 개발자가 데이터를 제공하고 각 항목의 모양을 정의하면 RecyclerView 라이브러리가 필요할 때 요소를 동적으로 생성합니다.
RecyclerView는 화면에서 스크롤된 새 항목의 뷰를 재사용합니다. 이렇게 뷰를 재사용하면 앱의 응답성을 개선하고 전력 소모를 줄이기 때문에 성능이 개선됩니다.
- 뷰홀더(ViewHolder)
- 어댑터(Adapter)
- 레이아웃 매니저(LayoutManager)
한마디로 설명하면 각 뷰를 보관하는 Holder 객체로 이야기 할 수 있습니다.
"ListView / RecyclerView 는 inflate를 최소화 하기 위해서 뷰를 재활용 하는데, 이 때 각 뷰의 내용을 업데이트 하기 위해 findViewById 를 매번 호출 해야합니다. 이로 인해 성능저하가 일어남에 따라 ItemView의 각 요소를 바로 엑세스 할 수 있도록 저장해두고 사용하기 위한 객체입니다." 정도로 설명할 수 있을 것 같습니다.
데이터를 받아서 관리하고 어댑터가 연결된 뷰에서 출력할 수 있는 형태로 그 데이터를 다시 제공하는 역할을 합니다.
리사이클러 뷰 레이아웃에 추가.
리사이클러 뷰 내에서 사용할 아이템 레이아웃 생성.
뷰홀더 클래스 구현.
어댑터 클래스 구현.
리사이클러뷰에 어댑터 적용.
레이아웃의 디자인 탭에서 추가하고 싶은곳에 리사이클러 뷰를 드래그해서 추가합니다.
리사이클러 뷰 내에서 반복적으로 사용할 아이템 레이아웃을 생성합니다.
class MyViewHolder private constructor (itemView: View): RecyclerView.ViewHolder(itemView) {
val idxText = itemView.findViewById<TextView>(R.id.idx_text)
val nameText = itemView.findViewById<TextView>(R.id.name_text)
val ageText = itemView.findViewById<TextView>(R.id.age_text)
fun bind(data: Data) {
idxText.text = data.idx
nameText.text = data.name
ageText.text = data.age
}
companion object {
fun from(parent: ViewGroup): MyViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val itemView = layoutInflater.inflate(R.layout.recycler_item_layout, parent, false)
return MyViewHolder(itemView)
}
}
}
아이템 레이아웃의 뷰를 포함하는 뷰홀더 클래스를 구현합니다.
class RecyclerAdapter(val item: MutableList<Data>): RecyclerView.Adapter<MyViewHolder>() {
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(item[position])
}
override fun getItemCount(): Int {
return item.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder.from(parent)
}
fun add() {
item += Data((item.size + 1).toString(), "seongjki", (item.size + 19).toString())
notifyItemChanged(itemCount)
}
}
뷰홀더를 생성하고 그에 맞는 데이터를 바인딩하는 어댑터 클래스를 생성합니다.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val items = mutableListOf<Data>()
val adapter = RecyclerAdapter(items)
binding.recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
binding.recyclerView.adapter = adapter
binding.button.setOnClickListener {
adapter.add()
}
}
}
findViewById는 뷰그룹에서 해당 id와 일치하는 뷰를 찾을 때까지 뷰그룹을 모두 탐색하는 함수 입니다.
따라서 뷰그룹 안에 또 뷰그룹이 있는 등, 복잡하게 구성된 레이아웃에서 사용될 경우 성능 저하를 일으킬 가능성이 있습니다.
이런 경우, findViewById 대신 뷰바인딩이나 데이터바인딩을 사용하면 이전에 설명한 문제를 예방할 수 있습니다.
아래는 바인딩을 사용해 findViewById를 대체한 코드의 예제입니다.
class MyViewHolder private constructor (binding: RecyclerItemLayoutBinding): RecyclerView.ViewHolder(binding.root) {
val idxText = binding.idxText
val nameText = binding.nameText
val ageText = binding.ageText
fun bind(data: Data) {
idxText.text = data.idx
nameText.text = data.name
ageText.text = data.age
}
companion object {
fun from(parent: ViewGroup): MyViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = RecyclerItemLayoutBinding.inflate(layoutInflater, parent, false)
return MyViewHolder(binding)
}
}
}
소스코드 요리사
안드로이드 공식 가이드
안드로이드 공식 코드랩
안드로이드 맛보기
WonseokOh의 velog