[Android / Kotlin] 리스너를 람다로 표현하기

Subeen·2024년 1월 29일
0

Android

목록 보기
52/73

RecyclerView를 구현할 때 아이템 클릭 이벤트를 사용하기 위해 Adapter 안에 인터페이스를 선언하고 인터페이스를 상속 받아 함수를 구현하여 사용했었다.
매번 이런식으로 인터페이스를 구현하여 사용하다보니 번거롭기도 하고 코드가 지저분해지는 것 같은 느낌이 들었는데, 이 부분을 람다로 표현하니 코드가 훨씬 간결해지고 보기 편해진 것 같다 ! 😆

기존에 사용하던 방식

class SearchListAdapter() : ListAdapter<SearchModel, SearchListAdapter.ViewHolder>(SearchDiffUtil) {

	// 아이템 클릭을 위한 인터페이스 선언 
    interface ItemClick {
        fun onClick(searchModel: SearchModel)
    }
    
    var itemClick: ItemClick? = null    

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding =
            ImageListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(binding, itemClickListener)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = getItem(position)
        holder.bind(item)
    }

    class ViewHolder(
        private val binding: ImageListItemBinding,
        private val itemClickListener: ((SearchModel) -> Unit)?
    ) :
        RecyclerView.ViewHolder(binding.root) {
        fun bind(item: SearchModel) = with(binding) {
			
            ivImage.setOnClickListener {
                itemClick?.onClick(item)
            }
        }
    }

}
	// Fragment 

	private val searchListAdapter by lazy {
		SearchListAdapter()
	}

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        initView()
    }
    
    fun initView() {
    	/*
         * Adapter에 아이템 클릭 리스너를 연결한다. 
         * SearchListAdapter.ItemClick의 인터페이스를 상속받아 함수를 구현한다. 
         */
        searchListAdapter.itemClick = object : SearchListAdapter.ItemClick {
            override fun onClick(searchModel: SearchModel) {
				// TODO 버튼 클릭 이벤트 
            }
        }
    }

람다로 표현한 방식

/*
 * (SearchModel) -> Unit : parameter 1개를 가진 함수
 * Unit : 반환 유형을 지정하지 않는 경우 기본적으로 Unit이 되며, 코틀린에서 값을 반환하지 않음을 의미한다. 
 */
class SearchListAdapter(
    private val itemClickListener: (SearchModel) -> Unit // 리스트의 아이템에 대한 클릭 리스너
) : ListAdapter<SearchModel, SearchListAdapter.ViewHolder>(SearchDiffUtil) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding =
            ImageListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(binding, itemClickListener) // 바인딩과 리스너를 사용하여 새로운 인스턴스 생성 
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = getItem(position)
        holder.bind(item)
    }

    class ViewHolder(
        private val binding: ImageListItemBinding,
        private val itemClickListener: ((SearchModel) -> Unit)?
    ) :
        RecyclerView.ViewHolder(binding.root) {
        fun bind(item: SearchModel) = with(binding) {
			
            ivImage.setOnClickListener {
                /*
                 * invoke는 마치 함수인 것처럼 호출할 수 있도록 하는 규칙이다. 
                 * (SearchModel) -> Unit를 호출하는 데 사용된다. 
                 */
                itemClickListener?.invoke(item)
            }
        }
    }

}
	// Fragment 
    
    private val searchListAdapter by lazy {
        SearchListAdapter(
        	// 람다식을 사용하면 함수를 선언할 필요가 없고, 코드 블럭을 직접 함수의 인자로 전달 할 수 있다.
            itemClickListener = { item ->
                // TODO 버튼 클릭 이벤트 
            }
        )
    }

invoke

  • 코틀린에는 invoke라는 함수, 정확히는 연산자가 존재한다. invoke 연산자는 이름 없이 호출 될 수있다.
class MyFunction {
    operator fun invoke(str: String): String {
        return str.uppercase()
    }
}

fun main() {
    val myfunction = MyFunction()
    println(myfunction.invoke("hello")) // hello
	println(myfunction("hello")) // hello
}
profile
개발 공부 기록 🌱

0개의 댓글