Skip to content

Instantly share code, notes, and snippets.

@tieorange
Last active September 18, 2023 07:03
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tieorange/97aed21a2633bceae13261c1c5948cbd to your computer and use it in GitHub Desktop.
Save tieorange/97aed21a2633bceae13261c1c5948cbd to your computer and use it in GitHub Desktop.
A debounced onClickListener for Android
package com.westwingnow.android.utils
import android.os.SystemClock
import android.view.View
import java.util.*
/**
* A Throttled OnClickListener
* Rejects clicks that are too close together in time.
* This class is safe to use as an OnClickListener for multiple views, and will throttle each one separately.
*
* * @param minimumIntervalMsec The minimum allowed time between clicks - any click sooner than this after a previous click will be rejected
*/
private const val minimumInterval = 1000L
class ThrottledOnClickListener(private val onClick: (view: View) -> Unit) : View.OnClickListener {
private val lastClickMap: MutableMap<View, Long> = WeakHashMap()
override fun onClick(clickedView: View) {
val previousClickTimestamp = lastClickMap[clickedView]
val currentTimestamp = SystemClock.uptimeMillis()
lastClickMap[clickedView] = currentTimestamp
if (previousClickTimestamp == null || currentTimestamp - previousClickTimestamp.toLong() > minimumInterval) {
onClick.invoke(clickedView)
}
}
}
@lukkystunt
Copy link

can you please give an example of how to use this class with a button. thank you

@kahakai
Copy link

kahakai commented Oct 7, 2019

can you please give an example of how to use this class with a button. thank you

val button: Button = view.findViewById(R.id.button)
val buttonClickListener = ThrottledOnClickListener {
    // do something
}
button.setOnClickListener(buttonClickListener)

You can also write an extension function to use this as button.setThrottledOnClickListener { }

@kahakai
Copy link

kahakai commented Oct 7, 2019

There is an error on 25 line, it's a recursive call. There needs to be onClick.invoke(clickedView) to use the lambda passed in the constructor.

@maiksonstrife
Copy link

Hi, how do i use it with multiples buttons ? My app is crashing

image

@lukkystunt
Copy link

I'll advise you use androidrx/rxjava library, it comes with throttle operator

@maiksonstrife
Copy link

Already solved, thanks for the great class

@steadymoka
Copy link

steadymoka commented Dec 16, 2019

I think, androidrx/rxjava library is too heavy for just debounce or throttle

@roskoff
Copy link

roskoff commented May 6, 2020

There is an error on 25 line, it's a recursive call. There needs to be onClick.invoke(clickedView) to use the lambda passed in the constructor.

Thumbs up for this.

@nikhilbansal97
Copy link

@artnest @tieorange
Is there a particular reason to create a separate class for it. Can't we do it as an extension function on View class? Something like this:

fun View.setDebouncedClickListener(
  debounceIntervalMs: Int = 700,
  listener: (view: View?) -> Unit
) {
  var lastTapTimestamp: Long = 0
  val customListener = View.OnClickListener {
    val currentTime = System.currentTimeMillis()
    if (currentTime - lastTapTimestamp > debounceIntervalMs) {
      lastTapTimestamp = currentTime
      listener(it)
    }
  }
  this.setOnClickListener(customListener)
}

@micadasystems
Copy link

There is an error on 25 line, it's a recursive call. There needs to be onClick.invoke(clickedView) to use the lambda passed in the constructor.

Works like a charm!

Maybe edit the gist to correct the error.

@tieorange
Copy link
Author

There is an error on 25 line, it's a recursive call. There needs to be onClick.invoke(clickedView) to use the lambda passed in the constructor.

Works like a charm!

Maybe edit the gist to correct the error.

Fixed. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment