android - 是否可以在侦听器中对 onClick 回调进行去抖动?
问题描述
我有一个用于放大和缩小地图视图的侦听器:
class ZoomMapListener(
mapView: MapView,
private val zoom: Zoom,
) : View.OnClickListener {
private val localMapView = WeakReference(mapView)
private var clickCount = 0
override fun onClick(view: View?) {
clickCount++
}
fun moveCamera() {
val mapView = localMapView.get()
mapView?.let {
var cameraPosition = it.map.cameraPosition
val zoom = if (zoom == IN) {
cameraPosition.zoom + (1.0f * clickCount)
} else {
cameraPosition.zoom - (1.0f * clickCount)
}
cameraPosition = CameraPosition(
cameraPosition.target,
zoom,
cameraPosition.azimuth,
cameraPosition.tilt,
)
clickCount = 0
it.map.move(cameraPosition, Animation(Animation.Type.SMOOTH, 0.5f), null)
}
}
}
enum class Zoom {
IN,
OUT
}
为了如果用户多次点击按钮,我决定使用另一个答案的去抖动运算符(https://stackoverflow.com/a/60234167/13236614),所以如果有五次点击,例如,相机会增加五倍在一次操作中。
扩展功能:
@FlowPreview
@ExperimentalCoroutinesApi
fun View.setDebouncedListener(
listener: ZoomMapListener,
lifecycleCoroutineScope: LifecycleCoroutineScope,
) {
callbackFlow {
setOnClickListener {
listener.onClick(this@setDebouncedListener)
offer(Unit)
}
awaitClose {
setOnClickListener(null)
}
}
.debounce(500L)
.onEach { listener.moveCamera() }
.launchIn(lifecycleCoroutineScope)
}
以及我如何在我的片段中使用它:
zoomInMapButton.setDebouncedListener(ZoomMapListener(mapView, Zoom.IN), lifecycleScope)
我认为这一切看起来有点糟糕,我怀疑是因为@FlowPreview
注释,所以有没有办法至少在自定义侦听器类中使它正确?
解决方案
使用带有@FlowPreview
or@ExperimentalCoroutinesApi
的东西有点像使用不推荐使用的函数,因为它可能会停止按预期工作或在库的未来版本中被删除。它们相对稳定,但每次更新核心 Kotlin 库时都需要检查它们。
我对其他问题的无协程回答更像是throttleFirst
,debounce
因为它不会延迟第一次点击。
我认为您只需更改一行代码即可直接在 ZoomListener 类中处理去抖动!替换clickCount++
为if (++clickCount == 1) v.postDelayed(::moveCamera, interval)
。
免责声明:我没有对此进行测试。
这里的策略是在第一次点击时立即发布延迟调用moveCamera()
。如果在该延迟时间内有任何点击,他们不会发布新的延迟呼叫,因为他们的贡献已计入延迟结束时将使用的clickCount
那个。moveCamera()
我也做了一些清理moveCamera()
,但它在功能上是一样的。在我看来,?.let
不应该用于局部变量,因为您可以利用局部变量的智能转换(或提前返回),这样您就可以使您的代码更具可读性和更少的嵌套。
class ZoomMapListener(
mapView: MapView,
private val zoom: Zoom,
private val interval: Long
) : View.OnClickListener {
private val localMapView = WeakReference(mapView)
private var clickCount = 0
override fun onClick(v: View) {
if (++clickCount == 1) v.postDelayed(::moveCamera, interval)
}
fun moveCamera() {
val map = localMapView.get()?.map ?: return
val multiplier = if (zoom == IN) 1f else -1f
val newCameraPosition = CameraPosition.builder(map.cameraPosition)
.zoom(map.cameraPosition.zoom + multiplier * clickCount)
.build()
clickCount = 0
map.move(newCameraPosition, Animation(Animation.Type.SMOOTH, 0.5f), null)
}
}
推荐阅读
- python - 通过访问可在常规浏览器上运行的页面,在 selenium 上禁止 403
- r - 根据另一个 data.frame 中的值创建新的 data.frame
- javascript - 从查询中检索工作项后访问 tfs 工作项的字段
- php - 如何使用深层变量创建多维数组
- dart - 在 Flutter 中将 Widget 放在 ListView 之上
- c++ - Visual Studio 2017 - 为依赖项禁用 CMake
- swift - 在类中创建自定义操作以在 Interface Builder 中使用
- c# - C#获取长字符串中所有出现的单词的所有内容
- c# - ASP.NET Core:标签助手“选择”为空
- active-directory - 不支持 SSAS 声明令牌