앞서 리사이클러뷰를 구현해 보았다. 그럼 이제 클릭 이벤트를 구현해 보자.
우선 onBindViewHolder메서드에 클릭 이벤트를 작성하자.
//AlbumRVAdapter.kt
// 뷰홀더에 데이터를 바인딩해줄때마다 호출, 클릭 이벤트 작성
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(albumList[position])
holder.itemView.setOnClickListener {
}
}
안드로이드 어플에선 어댑터의 외부, 즉 액티비티 또는 프래그먼트에서 아이템 클릭 이벤트를 처리하고 싶은 경우가 빈번하다. 그런데 리사이클러뷰에는 클릭리스너가 내장되어있지 않기 때문에 별도의 리스너 인터페이스를 만들어줘야 한다. 따라서 어댑터 클래스 내부에 다음 코드를 작성한다.
//리스너 인터페이스 정의
interface MyItemClickListener {
fun onItemClick(album: Album)
// fun onRemoveAlbum(position: Int)
}
그 다음 외부에 리스너 객체를 넘겨주어야 한다. 따라서 외부에서 전달받는 함수와 그 리스너 객체를 어댑터에서 사용할 수 있도록 따로 저장할 변수를 선언해주어야 한다. 따라서 어댑터 클래스에 다음 코드를 작성한다.
private lateinit var myItemClickListener: MyItemClickListener
fun setMyItemClickListener(itemClickListener: MyItemClickListener) {
myItemClickListener = itemClickListener
}
이렇게 외부에서 전달받을 수 있는 함수랑 그 반환값을 저장할 변수를 만들어주었다. 이제 어댑터 외부에 있는 홈 프래그먼트에서 리스너 객체를 던져주면 된다. 그 작업을 해 주자.
//HomeFragment.kt
albumRVAdapter.setMyItemClickListener(object : AlbumRVAdapter.MyItemClickListener {
override fun onItemClick(album: Album) {
}
이런 식으로 익명 객체를 써서 구현하였다. 이제 다시 onBindViewHolder메서드로 돌아오자. 뷰 홀더의 아이템이 클릭되었을 때, 클릭 리스너의 역할을 하는 인터페이스의 함수인 onItemClick함수를 호출하면 된다. 다음과 같이 작성해 주자
// 뷰홀더에 데이터를 바인딩해줄때마다 호출, 클릭 이벤트 작성
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(albumList[position])
holder.itemView.setOnClickListener { myItemClickListener.onItemClick() }
//holder.binding.itemAlbumTitleTv.setOnClickListener { myItemClickListener.onRemoveAlbum(position) }
}
그리고 홈 프래그먼트에 있는 onItemClick메서드를 오버라이딩해준다. 앨범프래그먼트로 전환되는 코드를 구현해 준다.
//HomeFragment.kt
//등록한 리사이클러뷰 어댑터 객체에 클릭리스너 세팅
albumRVAdapter.setMyItemClickListener(object : AlbumRVAdapter.MyItemClickListener {
override fun onItemClick(album: Album) {
changeAlbumFragment(album)
}
// override fun onRemoveAlbum(position: Int) {
// albumRVAdapter.removeItem(position)
// }
})
private fun changeAlbumFragment(album: Album) {
(context as MainActivity).supportFragmentManager.beginTransaction()
.replace(R.id.main_frm, AlbumFragment())
.commitAllowingStateLoss()
}
마지막으로 어댑터 클래스의 전체 코드를 보며 흐름을 정리해보자.
package com.example.flo.home
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.flo.databinding.ItemAlbumBinding
import com.example.flo.dataclasses.Album
class AlbumRVAdapter(private var albumList: ArrayList<Album>): RecyclerView.Adapter<AlbumRVAdapter.ViewHolder>() {
//리스너 인터페이스 정의
interface MyItemClickListener {
fun onItemClick(album: Album)
// fun onRemoveAlbum(position: Int)
}
private lateinit var myItemClickListener: MyItemClickListener
fun setMyItemClickListener(itemClickListener: MyItemClickListener) {
myItemClickListener = itemClickListener
}
fun addItem(album: Album) {
albumList.add(album)
notifyDataSetChanged()
}
// fun removeItem(position: Int) {
// albumList.removeAt(position)
// notifyDataSetChanged()
// }
override fun onCreateViewHolder(
viewGroup: ViewGroup,
viewType: Int
): ViewHolder {
val binding: ItemAlbumBinding = ItemAlbumBinding.inflate(
LayoutInflater.from(viewGroup.context), viewGroup, false
)
//아이템 뷰 객체를 던져줌
return ViewHolder(binding)
}
// 뷰홀더에 데이터를 바인딩해줄때마다 호출, 클릭 이벤트 작성
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(albumList[position])
holder.itemView.setOnClickListener { myItemClickListener.onItemClick(albumList[position]) }
//holder.binding.itemAlbumTitleTv.setOnClickListener { myItemClickListener.onRemoveAlbum(position) }
}
//데이터 세트 크기를 알려줌. 리사이클러뷰의 끝이 어딘지
override fun getItemCount(): Int = albumList.size
//뷰홀더 클래스
inner class ViewHolder(var binding: ItemAlbumBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(album: Album) {
binding.itemAlbumTitleTv.text = album.title
binding.itemAlbumSingerTv.text = album.singer
binding.itemAlbumCoverImgIv.setImageResource(album.coverImage!!)
}
}
}
이 코드는 디자인 패턴 중 콜백 패턴으로 만들어졌는데, 콜백 패턴이 뭔지 정리해보자
콜백 패턴은 어떤 객체가 자신이 직접 처리하지 않고, 다른 객체에게 처리를 위임하기 위해 함수를 전달하거나 인터페이스를 구현하게 하는 디자인 패턴으로, "이벤트가 발생하면 너가 대신 처리해줘!"라고 책임을 넘기는 구조이다.
콜백 패턴은 크게 3가지의 장점이 있다.
- 유연성 : 이벤트 처리 로직을 외부에서 자유롭게 처리 가능
- 재사용성 : 같은 컴포넌트를 다양한 상황에서 재사용 가능
- 결합도 낮춤 : 컴포넌트 간 의존성을 줄여 유지보수가 쉬움
어댑터는 클릭되었다는 것만 알려줌 -> 그걸 어떻게 처리할지는 너가 정해!(오버라이딩) 하고 외부에 위임 -> 외부에서 작업(앨범프래그먼트로의 전환) 실시
'안드로이드 스터디' 카테고리의 다른 글
| Retrofit과 백엔드와의 연동 (0) | 2025.12.30 |
|---|---|
| RecyclerView CRUD 구현하기 (2) | 2025.11.02 |
| RecyclerView를 써 보자! (0) | 2025.11.02 |
| 뷰페이저2를 만들어 보고 탭 레이아웃과 연동시켜보자! (0) | 2025.11.01 |
| HomeFragment.kt 분석 (0) | 2025.11.01 |