Как я могу отменить setOnClickListener в течение 1 секунды, используя Kotlin сопрограмм? - PullRequest
1 голос
/ 12 февраля 2020

Когда пользователь быстро нажимает на кнопку, метод showDialog () отображается несколько раз друг над другом, поэтому, когда вы отклоняете его, позади него появляется другой. Я ищу способ игнорировать второе касание в течение 1 секунды без использования обработчика или проверки времени предыдущего касания.

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

Я ищу решение с использованием Kotlin сопрограмм или Kotlin потоков для будущих реализаций.

Ответы [ 3 ]

3 голосов
/ 12 февраля 2020

Процедуры излишни для чего-то такого же тривиального, как и дебатирование:

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() }
3 голосов
/ 15 февраля 2020

Для этого лучше использовать простой Флаг вместо задержки, поскольку это не очень удобно для пользователя.

Но если вы хотите использовать сопрограммы, вы можете просто использовать Kotlin Поток Coroutine , чтобы применить это:

Сначала я создал Функция расширения для события click, которое возвращает Поток Coroutine. вот так:

    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'

Редактировать:

Аналог Flow оператора throttleFirst еще не реализован в сопрограммах 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 () для обработки этого. Полегче изменить значение этих параметров в соответствии с вашими потребностями.

1 голос
/ 12 февраля 2020

Я искренне рекомендую corbind

С этой замечательной библиотекой вы можете забыть о setOnClickListener и просто обрабатывать потоки, такие как

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

Он действительно прост в использовании, а с привязкой к представлению создание приложения становится очень простым. Я надеюсь, что это помогает, счастливое кодирование!

...