我有一个带有几个子级的父级布局,并且在父级上有一个长单击侦听器,并且几个子级有它自己的常规单击侦听器。我想在长按任何子项时实现一种行为,如果未消耗此事件,则父级可组合项将收到回调。是否可以?我知道我可以将 onLongClick 回调传递给所有孩子,但我想避免添加新孩子而有人忘记添加它的情况。我想以某种方式自动做到这一点。
我玩过这个,但无法弄清楚。示例代码
Box(
modifier = Modifier
.fillMaxWidth()
.combinedClickable(
onLongClick = {
Toast
.makeText(context, "Long click parent", Toast.LENGTH_SHORT)
.show()
},
onClick = {
Toast
.makeText(context, "Normal click parent", Toast.LENGTH_SHORT)
.show()
}
)
.padding(16.dp)
) {
Box(
modifier = Modifier
.height(100.dp)
.fillMaxWidth()
.background(color = Color.Green)
.clickable {
Toast
.makeText(context, "Normal click child", Toast.LENGTH_SHORT)
.show()
}
)
}
通过编写不消耗的自定义可点击对象,您不会拦截手势传播或消耗 PointerInputEvent,并且向下手势可以传播到父级以进行单击或长按,具体取决于按住指针的时间。
结果
手势
fun Modifier.clickableCustom(
onClick: () -> Unit,
) = composed {
val interactionSource = remember {
MutableInteractionSource()
}
clickableCustom(
interactionSource = interactionSource,
onClick = onClick
)
}
fun Modifier.clickableCustom(
interactionSource: MutableInteractionSource?,
onClick: () -> Unit,
) = this.then(
Modifier.pointerInput(Unit) {
coroutineScope {
awaitEachGesture {
val down = awaitFirstDown()
val pressInteraction = PressInteraction.Press(down.position)
interactionSource?.let {
launch {
interactionSource.emit(pressInteraction)
}
}
val up = waitForUpOrCancellation()
if (up != null) {
interactionSource?.let {
launch {
interactionSource.emit(PressInteraction.Release(pressInteraction))
}
}
onClick()
} else {
interactionSource?.let {
launch {
interactionSource.emit(PressInteraction.Cancel(pressInteraction))
}
}
}
}
}
}
)
演示
@OptIn(ExperimentalFoundationApi::class)
@Preview
@Composable
fun ClickPropagateSample() {
val context = LocalContext.current
Box(
modifier = Modifier
.fillMaxWidth()
.combinedClickable(
onLongClick = {
Toast
.makeText(context, "Long click parent", Toast.LENGTH_SHORT)
.show()
},
onClick = {
Toast
.makeText(context, "Normal click parent", Toast.LENGTH_SHORT)
.show()
}
)
.padding(16.dp)
) {
Box(
modifier = Modifier
.height(100.dp)
.fillMaxWidth()
.background(color = Color.Green)
.clickableCustom {
Toast
.makeText(context, "Normal click child", Toast.LENGTH_SHORT)
.show()
}
)
}
}