본문 바로가기

Kotlin_study

[Kotlin] RecyclerView와 ViewModel 적용해보기

ViewModel을 학습한 내용을 이를 이전에 만들었던 FLO 어플에 적용하면서 이에 대해 제대로 학습하고, HiStory 어플에 적용하려고 한다. 그래서 이번에는 RecyclerView와 ViewModel에 대해 학습하여 이를 적용해보았다.

 

//HomeFragment.kt
val albumRecyclerViewAdapter = AlbumRVAdapter(homeViewModel.albumList)
        binding.homeTodayRecyclerView.adapter = albumRecyclerViewAdapter
        albumRecyclerViewAdapter.setMyItemClickListener(object : AlbumRVAdapter.MyItemClickListener{
            override fun onItemClick(album: Album) {
                changeAlbumFragment(album)
                Log.d("home_Fragment", "change")
            }
            override fun onRemoveAlbum(position : Int) {
                albumRecyclerViewAdapter.removeItem(position)
            }
        })
//AlbumRVAdapter.kt
class AlbumRVAdapter(private val albumList: ArrayList<Album>) : RecyclerView.Adapter<AlbumRVAdapter.ViewHolder>(){

    // 클릭 인터페이스
    interface MyItemClickListener {
        fun onItemClick(album : Album)
        fun onRemoveAlbum(position: Int)
    }

    // 리스너 객체를 전달받는 함수랑 리스너 객체를 저장할 변수
    private lateinit var mItemClickListener: MyItemClickListener

    fun setMyItemClickListener(itemClickListener: MyItemClickListener){
        mItemClickListener = itemClickListener
    }
    // 뷰 홀더를 생성할 때 호출되는 함수 -> 아이템 뷰 객체를 만들어서 뷰 홀더에 넣어줌
    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
        val binding: ItemAlbumBinding = ItemAlbumBinding.inflate(LayoutInflater.from(viewGroup.context), viewGroup, false)

        return ViewHolder(binding)
    }
    fun addItems(albums: ArrayList<Album>) {
        albumList.clear()
        albumList.addAll(albums)
        notifyDataSetChanged()
    }

    fun addItem(album: Album){
        albumList.add(album)
        notifyDataSetChanged()
    }
    // notify 잊지말것!!
    fun removeItems() {
        albumList.clear()
        notifyDataSetChanged()
    }
    fun removeItem(position: Int) {
        albumList.removeAt(position)
        notifyDataSetChanged()
    }

    // 생성된 뷰 홀더에 데이터를 바인딩해줘야 할 때마다 호출됨
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(albumList[position])
    }

    // 데이터 세트 크기를 알려주는 함수, 리사이클러뷰가 마지막이 언제인지를 알 수 있음
    override fun getItemCount(): Int = albumList.size

    // 뷰홀더 : 아이템 객체들을 재활용 하기 위한 그릇
    inner class ViewHolder(val binding: ItemAlbumBinding) : RecyclerView.ViewHolder(binding.root){
        init {
            binding.itemAlbumImgIv.setOnClickListener  {
                mItemClickListener.onItemClick(albumList.get(adapterPosition))
                Log.d("AlbumRVA", "click${albumList.get(adapterPosition)}")
            }
        }
        fun bind(album : Album){
            binding.itemTitleTv.text = album.title
            binding.itemSingerTv.text = album.singer
           // binding.itemAlbumImgIv.setImageResource(album.coverImg!!)
        }

    }
}

이전에는 onBindViewHolder에서 클릭리스너를 생성해주었는데, 이 때 생길 수 있는 문제가 리사이클러뷰의 내용이 바뀔 때마다 해당 함수가 호출되기 때문에 리스너가 새롭게 생성되고 그 결과 memory leak 문제가 생길 수 있기 때문에 이를 ViewHolder에서 해주었다.

 

리사이클러뷰를 사용할 View(HomeFragment)에서 인터페이스를 정의해주었다. 그러면 AlbumRVAdapter에서 이를 클릭 리스너로 설정하면 해당 아이템을 클릭했을 때, 해당 뷰홀더에서 onItemClick()이 호출되고, album을 넘겨주면 이 값을 가지고 Fragment 전환이 이루어진다. 

 

코드는 깃허브 에서 확인하실 수 있습니다.