Kotlin Android debounce

前端 未结 10 841
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-05 11:07

Is there any fancy way to implement debounce logic with Kotlin Android?

I\'m not using Rx in project.

There is a way in Java, but it is too big

相关标签:
10条回答
  • 2020-12-05 11:18

    A more simple and generic solution is to use a function that returns a function that does the debounce logic, and store that in a val.

    fun <T> debounce(delayMs: Long = 500L,
                       coroutineContext: CoroutineContext,
                       f: (T) -> Unit): (T) -> Unit {
        var debounceJob: Job? = null
        return { param: T ->
            if (debounceJob?.isCompleted != false) {
                debounceJob = CoroutineScope(coroutineContext).launch {
                    delay(delayMs)
                    f(param)
                }
            }
        }
    }
    

    Now it can be used with:

    val handleClickEventsDebounced = debounce<Unit>(500, coroutineContext) {
        doStuff()
    }
    
    fun initViews() {
       myButton.setOnClickListener { handleClickEventsDebounced(Unit) }
    }
    
    0 讨论(0)
  • 2020-12-05 11:29

    For a simple approach from inside a ViewModel, you can just launch a job within the viewModelScope, keep track of the job, and cancel it if a new value arises before the job is complete:

    private var searchJob: Job? = null
    
    fun searchDebounced(searchText: String) {
        searchJob?.cancel()
        searchJob = viewModelScope.launch {
            delay(500)
            search(searchText)
        }
    }
    
    0 讨论(0)
  • 2020-12-05 11:33

    @masterwork's answer worked perfectly fine. Here it is for ImageButton with compiler warnings removed:

    @ExperimentalCoroutinesApi // This is still experimental API
    fun ImageButton.onClicked() = callbackFlow<Unit> {
        setOnClickListener { offer(Unit) }
        awaitClose { setOnClickListener(null) }
    }
    
    // Listener for button
    val someButton = someView.findViewById<ImageButton>(R.id.some_button)
    someButton
        .onClicked()
        .debounce(500) // 500ms debounce time
        .onEach {
            clickAction()
        }
        .launchIn(lifecycleScope)
    
    0 讨论(0)
  • 2020-12-05 11:34

    Using tags seems to be a more reliable way especially when working with RecyclerView.ViewHolder views.

    e.g.

    fun View.debounceClick(debounceTime: Long = 1000L, action: () -> Unit) {
        setOnClickListener {
            when {
                tag != null && (tag as Long) > System.currentTimeMillis() -> return@setOnClickListener
                else -> {
                    tag = System.currentTimeMillis() + debounceTime
                    action()
                }
            }
        }
    }
    

    Usage:

    debounceClick {
        // code block...
    }
    
    0 讨论(0)
提交回复
热议问题