RecyclerView이 무엇인지 설명하기 전에 등장하게 된 배경을 알아보겠습니다.
RecyclerView 이전에는 스크롤되는 리스트를 표현하기 위해서 ListView를 사용했습니다. ListView를 이용하면 간단하고 빠르게 리스트를 만들 수 있습니다.
하지만 이런 ListView에는 몇가지 단점이 있었습니다.
이런 문제점들을 해결하기 위해서 등장한 것이 RecyclerView입니다.
RecyclerView는 데이터 집합을 개별 아이템 단위로 구성하여 출력하는 ViewGroup이고, 한 화면에 표시되지 않는 많은 데이터를 스크롤 되는 리스트로 표시하는 역할을 합니다.
RecyclerView를 사용하는 가장 큰 이유는 재사용성이 좋기 때문입니다.
🔎 RecyclerView는 무엇을 재사용하는 것일까요?
ListView는 리스트를 스크롤하면 등장하는 데이터만큼 View를 생성합니다. 따라서 비효율적입니다. 스크롤이 되면 새롭게 나타나야 하는 View도 있지만, 더이상 나타날 필요가 없어지는 View도 있습니다. 그래서 RecyclerView는 바로 이 View를 재사용을 합니다.
View 객체 자체를 재사용하면서 View가 담고 있는 데이터만 바인딩합니다. 따라서 View의 생성과 삭제가 반복되는 것보다 성능과 비용 면에서 효과적으로 사용이 가능해진 것입니다.
이렇게 RecyclerView가 유연함을 가지도록 한 구조에 대해서 알아보겠습니다.
ViewHolder는 화면에 표시될 아이템 뷰를 저장하는 객체입니다.
RecyclerView는 몇 개의 뷰의 객체만 생성해서 재사용합니다. 그러기 위해서는 이런 뷰 객체를 기억하고 있을 객체가 필요하게 됩니다. 그것이 바로 ViewHolder입니다.
ViewHolder가 데이터를 가지고 있고, 그 데이터가 화면에 나타납니다. 스크롤을 내릴 때 이미 만들어진 ViewHolder가 존재한다면 데이터만 바인딩시켜서 사용하게 됩니다.
ListView에서도 성능 개선을 위해서 개발자가 직접 ViewHolder패턴을 적용시킬 수 있습니다.
RecyclerView는 다른 리스트를 표시하는 요소들과 마찬가지로 Adapter를 필요로 합니다.
Adapter는 리스트를 화면에 표시하기 위해서 아이템 단위로 View로 생성을 해서 RecyclerView에 바인딩 시키는 작업을 하는 객체입니다. 개발자가 직접 작성하여 RecyclerView에 연결시킵니다.
개발자는 RecyclerView.Adapter을 상속받아서 새로운 어댑터를 생성해야 하고, 다음 3개의 메서드를 오버라이드를 해야 합니다.
Adapter 함수의 작동 순서는 getItemCount → onCreateViewHolder → onBindViewHolder 입니다.
RecyclerView는 아이템 뷰를 수직, 수평, 격자(Grid) 형태의 레이아웃으로 배치할 수 있습니다. 이런 RecyclerView의 레이아웃을 관리하는 것이 LayoutManager입니다.
또한 더이상 화면에 표시되지 않는 아이템 뷰를 재활용하는 시점에 대한 정책도 결정합니다.
Activity에 RecyclerView 추가
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.favorite.MyFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
RecyclerView의 item View 레이아웃 추가
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_marginVertical="10dp"
android:layout_marginHorizontal="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp"/>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="32sp"/>
</LinearLayout>
RecyclerView Adapter, ViewHolder 구현
class FavoriteRecyclerAdapter(val items: ArrayList<Favorite>): RecyclerView.Adapter<FavoriteRecyclerAdapter.ViewHolder>(){
interface OnItemClickListener{
fun OnItemClick(url:String)
}
var itemClickListener:OnItemClickListener?=null
inner class ViewHolder(val binding: FavoriteRowBinding):RecyclerView.ViewHolder(binding.root){
init {
binding.root.setOnClickListener {
itemClickListener?.OnItemClick(items[adapterPosition].url)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = FavoriteRowBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.binding.apply {
textView.text = items[position].fName
imageView.setImageResource(items[position].id1)
}
}
override fun getItemCount(): Int {
return items.size
}
}
RecyclerView의 Adapter와 LayoutManager 지정
adapter = FavoriteRecyclerAdapter(items)
binding.recyclerView.apply {
addItemDecoration(decoration)
layoutManager = LinearLayoutManager(requireContext(),RecyclerView.VERTICAL, false)
adapter = this@MyFragment.adapter
}
RecyclerView는 ListView와 다르게 clickListener가 내장되어 있지 않습니다. 따라서 직접 구현을 해줘야합니다.
Adapter 안에 OnItemClickListenr를 interface로 만들고, ViewHolder에서 연결시키는 방법으로 구현을 해보겠습니다.
Adapter안에 interface로 clickListener 생성
class FavoriteRecyclerAdapter(val items: ArrayList<Favorite>): RecyclerView.Adapter<FavoriteRecyclerAdapter.ViewHolder>(){
interface OnItemClickListener{
fun onItemClick(url:String)
}
var itemClickListener:OnItemClickListener?=null
...
}
ViewHolder에서 clickListener 연결
inner class ViewHolder(val binding: FavoriteRowBinding):RecyclerView.ViewHolder(binding.root){
init {
binding.root.setOnClickListener {
itemClickListener?.onItemClick(items[adapterPosition].url)
}
}
}
Activity에서 Click이벤트 구현
```kotlin
class MainActivity : AppCompatActivity() {
...
adapter.itemClickListener = object : FavoriteRecyclerAdapter.OnItemClickListener{
override fun onItemClick(url: String) {
val intent = Intent(requireContext(), WebViewActivity::class.java)
intent.putExtra("url", url)
startActivity(intent)
}
}
}
```
Reference
https://recipes4dev.tistory.com/154