如何使用 Kotlin Coroutines 使 setOnClickListener 去抖动 1 秒?
How can I debounce a setOnClickListener for 1 second using Kotlin Coroutines?
当用户快速点击按钮时,showDialog() 方法会在彼此之上显示多次,因此当您关闭它时,它后面还有另一个。我正在寻找一种方法来忽略第二次点击 1 秒而不使用处理程序或检查前一次点击的时间。
//Button that opens a dialog
button.setOnClickListener {
showDialog()
}
我正在寻找使用 Kotlin 协程或 Kotlin 流的解决方案以供将来实施。
真心推荐corbind
有了这个很棒的库,您可以忘掉 setOnClickListener
而只处理像
这样的流程
binding.myButton.clicks()
.debounce(500)
.onEach { doSomethingImportant() }
.launchIn(viewLifecycleOwner.lifecycleScope)
它真的很容易使用,并且通过视图绑定让应用变得超级简单。希望对您有所帮助,编码愉快!
协程对于去抖这样微不足道的事情来说太过分了:
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))
用法:
myButton.setOnClickListener(1000L) { doSomething() }
最好使用简单的 Flag 而不是延迟,因为这不是好的用户体验。
但是如果你想使用协程,你可以简单地使用Kotlin Coroutine's Flow来应用这个:
首先,我为 returns 协程流的点击事件创建了一个 扩展函数 。像这样:
fun View.clicks(): Flow<Unit> = callbackFlow {
setOnClickListener {
offer(Unit)
}
awaitClose { setOnClickListener(null) }
}
现在,您只需像这样在 onCreate 中调用您的函数:
button.clicks().debounce(1000).onEach { println("clicked") }.launchIn(GlobalScope)
不要忘记在 build.gradle 文件中添加这些行:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
编辑:
throttleFirst 运算符的 Flow 模拟尚未在 kotlin 协程中实现。但是,可以在扩展函数的帮助下实现:
@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)
}
}
}
变化如下:
binding.button.clicks().throttleFirst(1250)
.onEach {
//delay(100)
showDialog()
}.launchIn(GlobalScope)
此外,您可以使用 delay() 来处理此问题。根据您的需要轻松更改这些参数的值。
当用户快速点击按钮时,showDialog() 方法会在彼此之上显示多次,因此当您关闭它时,它后面还有另一个。我正在寻找一种方法来忽略第二次点击 1 秒而不使用处理程序或检查前一次点击的时间。
//Button that opens a dialog
button.setOnClickListener {
showDialog()
}
我正在寻找使用 Kotlin 协程或 Kotlin 流的解决方案以供将来实施。
真心推荐corbind
有了这个很棒的库,您可以忘掉 setOnClickListener
而只处理像
binding.myButton.clicks()
.debounce(500)
.onEach { doSomethingImportant() }
.launchIn(viewLifecycleOwner.lifecycleScope)
它真的很容易使用,并且通过视图绑定让应用变得超级简单。希望对您有所帮助,编码愉快!
协程对于去抖这样微不足道的事情来说太过分了:
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))
用法:
myButton.setOnClickListener(1000L) { doSomething() }
最好使用简单的 Flag 而不是延迟,因为这不是好的用户体验。
但是如果你想使用协程,你可以简单地使用Kotlin Coroutine's Flow来应用这个:
首先,我为 returns 协程流的点击事件创建了一个 扩展函数 。像这样:
fun View.clicks(): Flow<Unit> = callbackFlow {
setOnClickListener {
offer(Unit)
}
awaitClose { setOnClickListener(null) }
}
现在,您只需像这样在 onCreate 中调用您的函数:
button.clicks().debounce(1000).onEach { println("clicked") }.launchIn(GlobalScope)
不要忘记在 build.gradle 文件中添加这些行:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
编辑:
throttleFirst 运算符的 Flow 模拟尚未在 kotlin 协程中实现。但是,可以在扩展函数的帮助下实现:
@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)
}
}
}
变化如下:
binding.button.clicks().throttleFirst(1250)
.onEach {
//delay(100)
showDialog()
}.launchIn(GlobalScope)
此外,您可以使用 delay() 来处理此问题。根据您的需要轻松更改这些参数的值。