如何使用 Kotlin 协程使 setOnClickListener 去抖动 1 秒?

Yan*_*tze 10 android kotlin debounce kotlin-coroutines kotlin-flow

当用户快速点击按钮时,showDialog() 方法会在彼此的顶部显示多次,因此当您关闭它时,它后面还有另一个。我正在寻找一种方法来忽略第二次点击 1 秒而不使用处理程序或检查前一次点击的时间。

//Button that opens a dialog
button.setOnClickListener {
    showDialog()
}
Run Code Online (Sandbox Code Playgroud)

我正在寻找使用 Kotlin 协程或 Kotlin 流程的解决方案以供将来实现。

Mor*_*aei 11

最好为此使用一个简单的Flag而不是延迟,因为这不是一个好的用户体验。

但是如果你想使用 Coroutines,你可以简单地使用Kotlin Coroutine's Flow来应用这个:

首先,我为返回协程流程的点击事件创建了一个扩展函数。像这样:

    fun View.clicks(): Flow<Unit> = callbackFlow {
    setOnClickListener {
        offer(Unit)
    }
    awaitClose { setOnClickListener(null) }
   } 
Run Code Online (Sandbox Code Playgroud)

现在,您只需要像这样在onCreate 中调用您的函数:

button.clicks().debounce(1000).onEach { println("clicked") }.launchIn(GlobalScope)
Run Code Online (Sandbox Code Playgroud)

不要忘记在 build.gradle 文件中添加这些行:

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
Run Code Online (Sandbox Code Playgroud)

编辑:

的流程模拟throttleFirst运营商未在科特林协程没有实现。但是,可以在扩展功能的帮助下实现:

@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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

变化如下:

binding.button.clicks().throttleFirst(1250)
        .onEach {
            //delay(100)
            showDialog()
        }.launchIn(GlobalScope)
Run Code Online (Sandbox Code Playgroud)

此外,您可以使用 delay() 来处理此问题。根据您的需要轻松更改这些参数的值。

  • 内部 debounce() 方法在您的情况下没有用。你应该定制它。我还编辑了使用throttleFirst Flow 运算符的答案。 (2认同)

Ten*_*r04 9

协程对于像 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))
Run Code Online (Sandbox Code Playgroud)

用法:

myButton.setOnClickListener(1000L) { doSomething() }
Run Code Online (Sandbox Code Playgroud)

  • 我应该澄清一下,这与去抖动并不完全相同。第一次点击没有延迟,我认为这对于响应能力更好。它更类似于“throttleFirst”。 (2认同)