list
를 보여주는 화면을 구현할 때 매번 사용하는 RecyclerView
. 좀 더 편하게 사용하기 위해 반복되는 코드를 줄여보자!
먼저 BaseClass
없이 구현되는 RecyclerView
를 viewBinding
을 이용해서 만들어 보자.
ViewHolder
에서 사용할 레이아웃을 만들어준다.
<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_position"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:paddingHorizontal="20dp"
tools:text="1" />
<TextView
android:id="@+id/tv_simple"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingVertical="10dp"
tools:text="테스트" />
</LinearLayout>
각 아이템 역할을 하는 ViewHolder
를 먼저 만들어준다.
생성자에서 itemView
를 설정 해주고 viewBinding
에 해당 itemView
를 넣어줬다.
init
블록에서 클릭 이벤트를 설정해 주었다.
onBindView()
에선 데이터를 받고 포지션과 전달받은 데이터를 설정해 주었다.
ViewHolder
생성 및 관리하는 Adapter
를 만들어준다. onCreateViewHolder()
, onBindViewHolder()
, getItemCount()
이 세 가지 함수는 필수로 구현해 줘야 한다.
onCreateViewHolder()
: ViewHolder
를 새로 만들어야 할 때 호출된다.onBindViewHolder()
: ViewHolder
를 데이터와 연결할 때 호출된다. 이를 통해 ViewHolder
의 레이아웃에 데이터를 채워준다.getItemCount()
: list
의 크기를 가져올 때 호출되며, list
의 총 크기를 설정해준다.getItemViewType()
: ViewHolder
의 레이아웃을 정해준다.XML에 RecyclerView
를 선언하고 layoutManager와 orientation을 설정해 주었다.
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_view_binding"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
RecyclerView
에 만들어 놓은 Adpater를 설정해 주면 된다.
val simpleAdapter = SimpleAdapter {
Toast.makeText(this, "Click $it", Toast.LENGTH_SHORT).show()
}
viewBinding.rvViewBinding.adapter = simpleAdapter
simpleAdapter.replaceAll(arrayListOf("가", "나", "다", "라", "마", "바"))
이제 BaseClass를 이용해 RecyclerView
를 만들어보자. (XML, Activity 설정은 생략)
어떤 데이터, ViewBinding
을 받을지 몰라 Generic
타입으로 정의해주었다.
생성자로 parent
와 layourRes
를 받아 itemView
를 설정해 주었다.
ViewHolder
의 레이아웃에 데이터를 채우기 위해 필요한 부분을 추상 메소드, 프로퍼티로 만들어 주었다.
viewBinding
: ViewHolder
의 레이아웃에 데이터를 세팅하기 위함.onBindView()
: Adapter
의 onBindViewHolder()
가 호출되었을 때 데이터를 받고 처리하기 위함.ViewHolder
와 동일하게 Generic
타입을 정의해주었고 파라미터로 layoutRes
를 받는다.
추상 메소드 두 가지를 추가해 준다.
onCreateViewBinding()
: ViewHolder
에 필요한 ViewBinding
을 생성하기 위함.onBindView()
: ViewHolder
의 bindView
호출을 위함.이제 override
한 함수를 살펴보자.
추상 클래스인 BaseViewHolder
를 생성해 준다. 그 과정에서 추상 프로퍼티, 메소드를 구현해야 하며 BaseApdater
의 추상 메소드를 설정해 주었다.
BaseViewHolder
의 추상 메소드 onBindView()
를 호출한다.
이제 추상 메소드 두 가지를 구현하는 작업을 진행하면 된다.
onCreateViewBinding()
: ViewHolder
가 생성될 때 호출될 것이고 ViewBinding
을 생성해 준다.
onBindView()
: Adpater
의 onBindViewHolder()
가 호출될 때 동작할 것이며 레이아웃에 데이터를 설정해 준다.
Base를 이용함으로써 구현해야 할 코드가 현저히 줄어든 것을 확인할 수 있었지만, 고민거리 하나가 생겼다.
notify
가 일어났을 때와 overScroll로 인해 ViewHolder
가 재사용 되면서 해당 데이터의 변경이 일어날 때 Adapter
의 onBindViewHolder()
가 호출되는데 현재 구현한 onBindView()
가 계속해서 호출되는 것이다.
onBindView()
구현부를 살펴보면 해당 뷰의 클릭이벤트를 설정해 주고 있고, 위에 했던 SimpleViewHolder
에서는 init
블락에서 설정해 주고 있다. onBindView()
는 계속해서 호출될 가능성이 높기 때문에 최초 init
블록에서 설정해 줘야 한다.
하지만 구현한 Adapter
는 ViewHolder
를 들고 있지 않고 Base에서 생성하기 때문에 init
블록에서 클릭 이벤트를 설정할 경우 데이터 변경이나 adapterPosition 변경을 대응하지 못한다.
고민 끝에 Adapter
의 ViewHolder
생성을 Base에서 제거하기로 했다.
기존 BaseViewHolder
는 동일하고 BaseAdapter
만 수정을 진행해 준다.
기존 추상 메소드와 onCreateViewHolder()
메소드를 제거해 준다.
BaseViewHolder
를 구현한 클래스와 Adapter
의 onCreateViewHolder()
메소드를 구현해 준다.
추상 프로퍼티와 메소드를 구현해 주고 init
블록에 클릭 이벤트를 등록해 준다.
위에서 만든 ViewHolder
클래스를 이용해 onCreateViewHolder()
메소드를 구현해 준다.
다양한 방법을 시도하고 고민한 끝에 Base 클래스를 만들 수 있었다. 추후 개발을 진행하면서 더 개선할 수 있는 방안을 찾아봐야겠다. 해당 샘플은 깃헙 레파지토리에서 확인해 볼 수 있다.
좋은 글 감사합니다~