以下代码适用于 Jetbrains Desktop Compose。它显示了一张带有按钮的卡片,现在如果您单击该卡片,“单击的卡片”将回显到控制台。如果您单击该按钮,它将回显“已单击按钮”
但是,我正在寻找一种方法让卡检测按钮的点击。我想在不更改按钮的情况下执行此操作,这样按钮就不需要知道它所在的卡。我希望这样做,以便卡片知道其表面上的某些内容已被处理,例如显示不同颜色的边框。
期望的结果是,当您单击按钮时,日志将回显“单击卡”和“单击按钮”行。我明白为什么mouseClickable
未调用,按钮声明已处理单击。所以我预计我需要使用另一种鼠标方法mouseClickable
。但我一生都无法弄清楚我应该使用什么。
@OptIn(ExperimentalComposeUiApi::class, androidx.compose.foundation.ExperimentalDesktopApi::class)
@Composable
fun example() {
Card(
modifier = Modifier
.width(150.dp).height(64.dp)
.mouseClickable { println("Clicked card") }
) {
Column {
Button({ println("Clicked button")}) { Text("Click me") }
}
}
}
当按钮找到点击事件时,它会将其标记为已消耗,这会阻止其他视图接收它。这是用完成的consumeDownChange()
,你可以看到detectTapAndPress
完成此操作的方法Button
here https://github.com/androidx/androidx/blob/da0944806932662865db1602128f5be25f81a5fa/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TapGestureDetector.kt#L201
要覆盖默认行为,您必须重新实现一些手势跟踪。与系统相比的变化列表detectTapAndPress
:
- I use
awaitFirstDown(requireUnconsumed = false)
而不是默认的requireUnconsumed = true
确保我们得到均匀的消耗
- 我用我自己的
waitForUpOrCancellationInitial
代替waitForUpOrCancellation
: 我这里用的是awaitPointerEvent(PointerEventPass.Initial)
代替awaitPointerEvent(PointerEventPass.Main)
,以便即使其他视图会获取事件也能获取该事件。
- Remove
up.consumeDownChange()
允许按钮处理触摸。
最终代码:
suspend fun PointerInputScope.detectTapAndPressUnconsumed(
onPress: suspend PressGestureScope.(Offset) -> Unit = NoPressGesture,
onTap: ((Offset) -> Unit)? = null
) {
val pressScope = PressGestureScopeImpl(this)
forEachGesture {
coroutineScope {
pressScope.reset()
awaitPointerEventScope {
val down = awaitFirstDown(requireUnconsumed = false).also { it.consumeDownChange() }
if (onPress !== NoPressGesture) {
launch { pressScope.onPress(down.position) }
}
val up = waitForUpOrCancellationInitial()
if (up == null) {
pressScope.cancel() // tap-up was canceled
} else {
pressScope.release()
onTap?.invoke(up.position)
}
}
}
}
}
suspend fun AwaitPointerEventScope.waitForUpOrCancellationInitial(): PointerInputChange? {
while (true) {
val event = awaitPointerEvent(PointerEventPass.Initial)
if (event.changes.fastAll { it.changedToUp() }) {
// All pointers are up
return event.changes[0]
}
if (event.changes.fastAny { it.consumed.downChange || it.isOutOfBounds(size) }) {
return null // Canceled
}
// Check for cancel by position consumption. We can look on the Final pass of the
// existing pointer event because it comes after the Main pass we checked above.
val consumeCheck = awaitPointerEvent(PointerEventPass.Final)
if (consumeCheck.changes.fastAny { it.positionChangeConsumed() }) {
return null
}
}
}
附:你需要添加implementation("androidx.compose.ui:ui-util:$compose_version")
对于 Android Compose 或implementation(compose("org.jetbrains.compose.ui:ui-util"))
为桌面撰写到您的build.gradle.kts
to use fastAll
/fastAny
.
Usage:
Card(
modifier = Modifier
.width(150.dp).height(64.dp)
.clickable { }
.pointerInput(Unit) {
detectTapAndPressUnconsumed(onTap = {
println("tap")
})
}
) {
Column {
Button({ println("Clicked button") }) { Text("Click me") }
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)