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.
debounce helps to detect the state when no new data is submitted for some time, effectively allowing you to process a data when the input is completed. fun <T> debounce( waitMs: Long = 300L, coroutineScope: CoroutineScope, destinationFunction: (T) -> Unit ): (T) -> Unit { var debounceJob: Job? =
Definition of runBlocking() functionRuns a new coroutine and blocks the current thread interruptible until its completion. This function should not be used from a coroutine. It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in main functions and in tests.
Once the withContext block finishes, the coroutine in login() resumes execution on the main thread with the result of the network request.
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() }
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With