Recyclerview는 정말 자주 사용하는 기능 중 하나이기 때문에, 미리 규격화하여 재활용하여 사용하는것이 좋다.
interface BoardListListener: AdapterListener {
fun onClickItem(model: BoardListModel)
}
-
abstract class ModelViewHolder<M: Model>(
binding: ViewBinding,
protected val viewModel: BaseViewModel,
protected val resourcesProvider: ResourcesProvider
): RecyclerView.ViewHolder(binding.root) {
abstract fun reset()
open fun bindData(model: M){
reset()
}
abstract fun bindViews(model: M, adapterListener: AdapterListener)
}
-
class BoardViewHolder(
private val binding:ViewholderBoardBinding,
viewModel: BaseViewModel,
resourcesProvider: ResourcesProvider
): ModelViewHolder<BoardListModel>(binding, viewModel, resourcesProvider) {
override fun reset() = with(binding) {
}
override fun bindViews(model: BoardListModel, adapterListener: AdapterListener) = with(binding) {
if(adapterListener is BoardListListener) {
root.setOnClickListener {
//todo
}
}
}
override fun bindData(model: BoardListModel) {
super.bindData(model)
with(binding) {
nameTextView.text = model.name
titleTextView.text = model.title
}
}
}
Viewholder의 정보를 담을 추상클래스를 작성하고 상속받아 사용한다.
bindViews: model은 하나의 viewholder에 들어갈 정보들을 구성하는 data class를 주로 사용하고, 각 객체등의 동작(onClick등)들을 구현하는 adapterListener를 담는다.
bindData: 실제 layout의 위젯들과 model의 데이터를 bind한다.
object ModelViewHolderMapper {
@Suppress("UNCHECKED_CAST")
fun <M: Model> map(
parent: ViewGroup,
type: CellType,
viewModel: BaseViewModel,
resourcesProvider: ResourcesProvider
): ModelViewHolder<M> {
var inflater = LayoutInflater.from(parent.context)
val viewHolder = when (type) {
CellType.ARTICLE_CELL -> BoardViewHolder(
ViewholderBoardBinding.inflate(inflater, parent, false),
viewModel,
resourcesProvider
)
}
return viewHolder as ModelViewHolder<M>
}
}
recyclerview에 담을 viewholder를 사전에 정의해둔 CellType이라는 enum class를 기준으로 mapping하는 클래스
하나의 adpater에 담는 모델들의 CellType만을 변경해주는것만으로 편하게 viewholder를 바꿀 수 있다.
class ModelRecyclerAdapter<M: Model, VM: BaseViewModel>(
private var modelList: List<Model>,
private val viewModel: VM,
private val resourcesProvider: ResourcesProvider,
private val adapterListener: AdapterListener
): ListAdapter<Model, ModelViewHolder<M>>(Model.DIFF_CALLBACK) {
override fun getItemCount(): Int = modelList.size
override fun getItemViewType(position: Int): Int = modelList[position].type.ordinal
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ModelViewHolder<M> {
return ModelViewHolderMapper.map(parent, CellType.values()[viewType], viewModel, resourcesProvider)
}
@Suppress("UNCHECKED_CAST")
override fun onBindViewHolder(holder: ModelViewHolder<M>, position: Int) {
with(holder) {
bindData(modelList[position] as M)
bindViews(modelList[position] as M, adapterListener)
}
}
override fun submitList(list: List<Model>?) {
list?.let { modelList = it }
super.submitList(list)
}
}
재활용하여 사용할 recyclerview adapter를 작성한다.
recyclerview adapter의 기본 사용방법에 대한 기록은 생략.
private val adapter by lazy {
ModelRecyclerAdapter<BoardListModel, ArticleListViewModel> (
listOf(),
viewModel,
resourcesProvider,
adapterListener = object : BoardListListener {
override fun onClickItem(model: BoardListModel) {
//TODO
}
}
)
}
위처럼 adpater를 정의하여 사용하면 된다.
코드 전문
https://github.com/YeseopLee/AllofMe/tree/feature/ArticleListFragment
Reference
https://github.com/Fastcampus-Android-Lecture-Project-2021/aop-part6-chapter01
'Android > Tips' 카테고리의 다른 글
툴바(ToolBar) 구현, Toolbar VS Layout (0) | 2021.12.13 |
---|---|
RecyclerView에 animation 효과 주기 (0) | 2021.12.04 |
TabLayout (0) | 2021.11.15 |
Retrofit2 Multiple BaseUrl (Java) (0) | 2021.03.25 |
ViewPager2 오버랩 화면전환 (pageTransformer) (0) | 2021.01.05 |