Android 开发中点击事件的处理一般是网络请求、弹出提示、跳转界面。
如果用户快速连续点击两次,就会请求两次网络,或是弹出两次提示等。
为了避免这种情况的方法,需要做点击事件防抖,即在一定时间间隔内只响应第一次点击事件。
方式一
利用 view 的 clickable:
inline fun View.setThrottleListener(
thresholdMillis: Long = 500L,
crossinline block: () -> Unit
) {
setOnClickListener {
isClickable = false
postDelayed({ isClickable = true }, thresholdMillis)
block.invoke()
}
}
// 使用方式
mBinding.btn.setThrottleListener {
// 处理点击逻辑
}
方式二
在 view 的 tag 中记录最后一次点击时间,首先在 ids.xml 中添加:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="clickLastTimeKey" type="id" />
</resources>
var View.clickLastTime: Long
get() = getTag(R.id.clickLastTimeKey) as? Long ?: 0
set(value) = setTag(R.id.clickLastTimeKey, value)
fun View.setThrottleListener(thresholdMillis: Long = 500, block: () -> Unit) {
setOnClickListener {
val currentTime = SystemClock.elapsedRealtime()
if (currentTime - clickLastTime >= thresholdMillis) {
clickLastTime = currentTime
block.invoke()
}
}
}
// 使用
mBinding.btn.setThrottleListener {
// 处理点击逻辑
}
方式三
同样是记录最后一次点击时间,不过是使用 flow:
fun <T> Flow<T>.throttleFirst(thresholdMillis: Long): Flow<T> = flow {
var lastTime = 0L
collect { upstream ->
val currentTime = SystemClock.elapsedRealtime()
if (currentTime - lastTime >= thresholdMillis) {
lastTime = currentTime
emit(upstream)
}
}
}
@ExperimentalCoroutinesApi
fun View.clickFlow() = callbackFlow {
setOnClickListener { trySend(Unit).onFailure { e -> e?.printStackTrace() } }
awaitClose { setOnClickListener(null) }
}
// 使用
mBinding.btn.clickFlow().throttleFirst(500)
.onEach {} // 处理点击逻辑
.launchIn(lifecycleScope)