리사이클러뷰(RecyclerView)
리사이클러뷰는 안드로이드에서 리스트를 만들기 위해 사용되는 뷰 입니다.
리스트를 만들기 위해 사용된다는 점에서 리스트뷰(ListView)와 비슷하지만,
뷰를 재활용(Recycler)한다는 점에서 리스트뷰보다 더 개선된 형태입니다.
하지만, 뷰를 재활용 하기 때문에 각 뷰의 내용을 담아둘 뷰 홀더(View Holder)가 필요합니다.
아이템 레이아웃 만들기
<?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"
android:paddingHorizontal="15dp"
android:paddingVertical="20dp">
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="13:42" />
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingHorizontal="10dp"
android:text="글 제목" />
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="홍길동" />
</LinearLayout>
item_recycler_view.xml
아이템의 각 행을 담을 레이아웃을 만듭니다.
데이터 클래스 만들기
data class BoardItem(val time: String, val title: String, val name: String)
리스트의 각 아이템의 내용이 담길 데이터 클래스를 하나 만듭니다.
저는 예시로 글 작성 시간, 글 제목, 글 작성자를 담은 데이터클래스를 생성하였습니다.
리사이클러뷰 어댑터 만들기
class BoardAdapter(val itemList: ArrayList<BoardItem>) :
RecyclerView.Adapter<BoardAdapter.BoardViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BoardViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_recycler_view, parent, false)
return BoardViewHolder(view)
}
override fun onBindViewHolder(holder: BoardViewHolder, position: Int) {
holder.tv_time.text = itemList[position].time
holder.tv_title.text = itemList[position].title
holder.tv_name.text = itemList[position].name
}
override fun getItemCount(): Int {
return itemList.count()
}
inner class BoardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tv_time = itemView.findViewById<TextView>(R.id.tv_time)
val tv_title = itemView.findViewById<TextView>(R.id.tv_title)
val tv_name = itemView.findViewById<TextView>(R.id.tv_name)
}
}
어댑터 클래스는 RecyclerView.Adapter<ViewHolder>를 상속받고,
onCreateViewHolder, onBindViewHolder, getItemCount를 각각 오버라이딩 해줍니다.
ViewHolder
inner class BoardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tv_time = itemView.findViewById<TextView>(R.id.tv_time)
val tv_title = itemView.findViewById<TextView>(R.id.tv_title)
val tv_name = itemView.findViewById<TextView>(R.id.tv_name)
}
ViewHolder는 어댑터 클래서 외부 혹은 내부에 RecyclerView.ViewHolder클래스를 상속하여 만듭니다.
각 뷰들을 itemView.findViewById를 사용하여 해당 뷰를 연결합니다.
onCreateViewHolder
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BoardViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_recycler_view, parent, false)
return BoardViewHolder(view)
}
onCreateViewHolder 메소드는 뷰가 만들어질때(create) 호출되는 메소드입니다.
위에서 만든 각 행 레이아웃(R.layout.item_recycler_view)를 인플레이트하여 뷰 홀더를 생성해 return 해줍니다.
onBindViewHolder
override fun onBindViewHolder(holder: BoardViewHolder, position: Int) {
holder.tv_time.text = itemList[position].time
holder.tv_title.text = itemList[position].title
holder.tv_name.text = itemList[position].name
}
onBindViewHolder는 뷰가 바인드(Bind)될때 호출되는 메소드입니다.
onCreateViewHolder에서 뷰가 만들어졌다면, onBindViewHolder는 뷰에 내용이 씌워질때라 보시면 될 것 같습니다.
리사이클러뷰가 한번 만들어지면, 각 뷰는 한번만 create되지만, 삭제되지 않고 계속 재활용(recycle)되기 때문에,
onCreateViewHolder는 리사이클러뷰가 만들어질때만 호출되나,
onBindViewHolder는 스크롤을 내리거나 올릴때마다 호출됩니다.
getItemCount
override fun getItemCount(): Int {
return itemList.count()
}
getItemCount는 리스트의 수를 return 합니다.
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">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_board"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_recycler_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
리사이클러뷰 속성에
tools:listitem="@layout/item_recycler_view"
를 추가하면 해당 아이템으로 리사이클러뷰를 볼 수 있습니다.
tools 속성은 xml에서만 보일 뿐, 실제 앱 동작 상에선 영향을 끼치지 않습니다.
리사이클러뷰에 어댑터 붙이기
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rv_board = findViewById<RecyclerView>(R.id.rv_board)
val itemList = ArrayList<BoardItem>()
itemList.add(BoardItem("13:00","월급 두배로 받는법","김XX"))
itemList.add(BoardItem("11:00","학점 A+ 받는 법","이XX"))
itemList.add(BoardItem("10:00","구글 면접 질문에 대답하는 법","박XX"))
itemList.add(BoardItem("08:00","공부 잘하는 MBTI 순위","최XX"))
val boardAdapter = BoardAdapter(itemList)
boardAdapter.notifyDataSetChanged()
rv_board.adapter = boardAdapter
rv_board.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
}
}
val itemList = ArrayList<BoardItem>()
itemList.add(BoardItem("13:00","월급 두배로 받는법","김XX"))
itemList.add(BoardItem("11:00","학점 A+ 받는 법","이XX"))
itemList.add(BoardItem("10:00","구글 면접 질문에 대답하는 법","박XX"))
itemList.add(BoardItem("08:00","공부 잘하는 MBTI 순위","최XX"))
위에서 만든 BoardItem으로 ArrayList를 만들어주고, 샘플데이터를 몇개 넣어봤습니다.
val boardAdapter = BoardAdapter(itemList)
boardAdapter.notifyDataSetChanged()
ArrayList를 사용해 Adapter를 만들고,
notifyDataSetChange()를 이용해 어댑터와 리사이클러뷰를 갱신시켜줍니다.
rv_board.adapter = boardAdapter
rv_board.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
마지막으로 리사이클러뷰에 어댑터를 붙여주고,
레이아웃 매니저를 설정해줍니다.
위에서는 LinearLayoutManager를 사용해 설정해주었습니다.
최종 완성본
위와 같이 잘 나오는 것을 볼 수 있습니다.
이 예제에서는 수직 리사이클러뷰면 알아봤지만,
사용방법에 따라 수평 혹은 그리드 형태로도 만들 수 있습니다.
후기
지금까지 RecylcerView를 한번 만들어 봤습니다.
ListView에 비해 사용방법이 어렵고 복잡하게 느껴질 수 있습니다.
그러나, 뷰를 재활용 하기 때문에 자원적으로 더 효율적이며, 더 유연하게 작동합니다.
앱에서 꽤 많이 사용되는 위젯이기 때문에 한 번 사용법을 알아두면 꽤나 요긴하게 쓰입니다.
또한, RecylcerView는 ListView와 달리, 아이템 클릭 리스너를 기본적으로 지원하지 않는데,
이와 관련된 내용은 아래 포스팅을 참고해주세요.
https://uknowblog.tistory.com/125