[Android] Kotlin Extension Function을 사용하여 더블 클릭 방지하기

[Android] Kotlin Extension Function을 사용하여 더블 클릭 방지하기

코틀린 확장 함수를 통해 View 더블 클릭 방지를 쉽게 구현할 수 있습니다.

보통 더블 클릭 방지를 위해 특정 시간을 정해두고 한 번 클릭 후 해당 시간 이내의 클릭 이벤트는 무시하는 방법을 사용합니다. 직접 전역 Util 함수를 만들어 사용하거나 RxBinding의 throttle을 이용하는 방법도 있습니다.

Util 함수를 사용하는 방법은 onClick()마다 boilerplate가 생기게 되고, 깜빡하고 추가를 하지 않은 경우도 생기기 쉽습니다.

backButton.setOnClickListener {
    if (ExampleUtils.isDoubleClick()) return@setOnClickListener
    onBackPressed()
}

RxBinding 사용 또한 clicks()와 함께 throttle()를 추가해주는 것을 깜빡할 수 있고, 3rd party 라이브러리를 추가해야 합니다.

backButton.clicks()
    .throttleFirst(500, TimeUnit.MILLISECONDS)
    .subscribe { onBackPressed() }

Kotlin 확장 함수로 더블 클릭 방지 구현하기

1. ClickListener 구현하기

View.OnClickListener를 구현하여 위에서 설명한 방법으로 예외처리를 추가합니다. 저의 경우 RxBinding을 사용할 필요를 느끼지 못해서 직접 시간 측정하는 방법을 사용했습니다.

코틀린은 함수도 파라미터로 전달할 수 있기 때문에, 인터페이스를 생성하지 않고 생성자로 직접 클릭 처리 함수를 받도록 했습니다.

class OnSingleClickListener(private val onSingleClick: (View) -> Unit) : View.OnClickListener {
    companion object {
        private const val CLICK_INTERVAL = 500
    }

    private var lastClickedTime: Long = 0L

    override fun onClick(v: View?) {
        if (isSafe() && v != null) {
            lastClickedTime = System.currentTimeMillis()
            onSingleClick(v)
        }  
    }

    private fun isSafe(): Boolean {
        return System.currentTimeMillis() - lastClickedTime > CLICK_INTERVAL
    }
}

2. 확장 함수 정의하기

만약 확장 함수를 사용하지 않는다면, View.setOnClickListener를 사용할 때마다 OnSingleClickListener를 생성하여 넣어줘야 합니다.

fun View.setOnSingleClickListener(onSingleClick: (View) -> Unit) {
    val singleClickListener = OnSingleClickListener { onSingleClick(it) }
    setOnClickListener(singleClickListener)
}

3. 사용할 때는 setOnClickListener처럼 사용해요.

실제로 View에서는 기존 setOnClickListener를 사용했던 것과 동일하게 쓸 수 있습니다.

textView.setOnSingleClickListener { onClickItem() }

이렇게 쓰더라도 setOnSafeClickListener 대신 그냥 setOnClickListener를 사용해버리는 경우가 분명히 있을 수 있습니다. 하지만 refactor tool(ctrl + shift + r)을 이용해 setOnClickListenersetOnSingleClickListener로 바꾸기만 하면 되니 다른 방법들보다 수정이 비교적 편리합니다.


추가로 확장 함수를 많이 사용하게 된다면 종류별로 Extention Function을 모아서 관리하는 것을 추천합니다. 코틀린은 파일 == 클래스 가 아니니까요! 예를 들면, ViewExtentions 에는 View 관련 확장 함수들 / CollectionExtensions에는 collection 관련 확장 함수들 이렇게요. 확장 함수가 늘어난다면, TextViewExtensions, ButtonExtensions / ListExtensions, MapExtensions 등 처럼 더 상세하게 쪼갤 수도 있을 것 같습니다.

이외에도 확장 함수를 잘 활용한다면 이름처럼 확장성있는 프로그래밍이 가능할 것 같습니다!

Comments