How can I debounce a setOnClickListener for 1 second using Kotlin Coroutines?

蹲街弑〆低调 提交于 2020-07-19 04:47:12

问题


When user taps fast on the button the showDialog() method displays multiple times on top of each other, so when you dismiss it there is another one behind it. I am looking for a way to ignore the second tap for 1 second without using a handler or check the previous tap's time.

//Button that opens a dialog
button.setOnClickListener {
    showDialog()
}

I am looking for a solution using Kotlin coroutines or Kotlin flows for future implementations.


回答1:


It's better to use a simple Flag for that instead of delay as it's not a good user experience.

But if you want to use Coroutines, You can simply use Kotlin Coroutine's Flow to apply this:

First I created an Extension Function for the click event that returns a Coroutine's Flow. like this:

    fun View.clicks(): Flow<Unit> = callbackFlow {
    setOnClickListener {
        offer(Unit)
    }
    awaitClose { setOnClickListener(null) }
   } 

Now, All you need is Calling your Function in onCreate like this:

button.clicks().debounce(1000).onEach { println("clicked") }.launchIn(GlobalScope)

Don't forget to add these lines in build.gradle file:

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'

Edit:

The Flow analogue of throttleFirst operator is not implemented yet in kotlin coroutines. however, can be implemented with the help of Extension Functions:

@FlowPreview
@ExperimentalCoroutinesApi
fun <T> Flow<T>.throttleFirst(windowDuration: Long): Flow<T> = flow {
    var lastEmissionTime = 0L
    collect { upstream ->
        val currentTime = System.currentTimeMillis()
        val mayEmit = currentTime - lastEmissionTime > windowDuration
        if (mayEmit)
        {
            lastEmissionTime = currentTime
            emit(upstream)
        }
    }
}

The changes are as follows:

binding.button.clicks().throttleFirst(1250)
        .onEach {
            //delay(100)
            showDialog()
        }.launchIn(GlobalScope)

Also, you can use a delay() to handle this. Take it easy to change value of these parameters according to your needs.




回答2:


Couroutines are overkill for something as trivial as debounce:

class DebounceOnClickListener(
    private val interval: Long,
    private val listenerBlock: (View) -> Unit
): View.OnClickListener {
    private var lastClickTime = 0L

    override fun onClick(v: View) {
        val time = System.currentTimeMillis()
        if (time - lastClickTime >= interval) {
            lastClickTime = time
            listenerBlock(v)
        }
    }
}

fun View.setOnClickListener(debounceInterval: Long, listenerBlock: (View) -> Unit) =
    setOnClickListener(DebounceOnClickListener(debounceInterval, listenerBlock))

Usage:

myButton.setOnClickListener(1000L) { doSomething() }



回答3:


I honestly recommend corbind

With this great library you can forget about setOnClickListener and just handle flows like

binding.myButton.clicks() .debounce(500) .onEach { doSomethingImportant() } .launchIn(viewLifecycleOwner.lifecycleScope)

It's really easy to use, and with view binding making an app becomes super simple. I hope it helps, happy coding!



来源:https://stackoverflow.com/questions/60190103/how-can-i-debounce-a-setonclicklistener-for-1-second-using-kotlin-coroutines

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!