Jetpack Compose - 从子级到父级的长按传播

问题描述 投票:0回答:1

我有一个带有几个子级的父级布局,并且在父级上有一个长单击侦听器,并且几个子级有它自己的常规单击侦听器。我想在长按任何子项时实现一种行为,如果未消耗此事件,则父级可组合项将收到回调。是否可以?我知道我可以将 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()
            }
    )
}
android kotlin android-jetpack-compose gesture-recognition android-jetpack-compose-gesture
1个回答
0
投票

通过编写不消耗的自定义可点击对象,您不会拦截手势传播或消耗 PointerInputEvent,并且向下手势可以传播到父级以进行单击或长按,具体取决于按住指针的时间。

结果

enter image description here

手势

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()
                }
        )
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.