多个不同的指针输入

ame*_*cle 6 kotlin android-jetpack-compose

我目前正在尝试实现在可组合项可缩放、可平移(拖动表面)或两者之间切换的选项。到目前为止,有效的方法是切换相应的按钮,以达到预期的结果。不起作用的是切换一个按钮与另一个按钮 - 这会产生保留第一个按钮功能的意外结果。例如,假设缩放处于活动状态。当我按下平移按钮时,背景突出显示会相应变化,所有测试日志都显示预期状态 - 但表面仍然可缩放,不可拖动。我首先必须手动禁用缩放。关于为什么会发生这种情况有什么想法吗?

  Modifier.run {
            if (zoomEnabled) {
                this.pointerInput(Unit) {
                    detectTransformGestures { _, _, zoom, _ ->
                        passScale(zoom)
                    }
                }
            } else if (panEnabled) {
                this.pointerInput(Unit) {
                    detectDragGestures { change, dragAmount ->
                        change.consumeAllChanges()
                        passOffsetX(dragAmount.x / 3)    
                        passOffsetY(dragAmount.y / 3)    
                    }   
                }
            } else
                this
        }
Run Code Online (Sandbox Code Playgroud)

纽扣:

@Composable
fun TopBarAction(
    zoomEnabled: Boolean,
    passZoomEnabled: (Boolean) -> Unit,
    panEnabled: Boolean,
    passPanEnabled: (Boolean) -> Unit
) {
    IconToggleButton(
        checked = zoomEnabled,
        onCheckedChange = {
            passPanEnabled(false)
            passZoomEnabled(it)
        },
        modifier = Modifier
            .background(
                if (zoomEnabled) Color.LightGray else Color.Transparent,
                shape = CircleShape
            ),
        enabled = true
    ) {
        Icon(...)
    }
    IconToggleButton(
        checked = panEnabled,
        onCheckedChange = {
            passZoomEnabled(false)
            passPanEnabled(it)
        },
        modifier = Modifier
            .background(
                if (panEnabled) Color.LightGray else Color.Transparent,
                shape = CircleShape
            ),
        enabled = true
    ) {
        Icon(...)
    }
}
Run Code Online (Sandbox Code Playgroud)

使用普通的 .pointerInput 修饰符和内部条件而不是 run 根本无法识别任何输入,并且 detectorTransformGesture 的 pan 没有按照我需要的方式运行(尽管如果其他所有方法都失败,这可能是我将使用的)

Thr*_*ian 7

第一个问题是 PointerInput 使用一个或多个键创建一个闭包,并使用旧值,除非您设置的键发生变化。

您需要相应地设置键。

第二个问题是,即使您设置了键, detectorDragGestures 或 detectorTransformGestures 也会消耗事件,因此如果第一个已经消耗了事件,则上面的 PointerInputChange 将无法获取它。

consume()的作用是通过返回Offset.ZerotrueconsumeAllChanges()来阻止其上方或父级上的 pointInput 接收事件。由于拖动、滚动或变换手势会检查 PointeInputChange.isConsumed 是否为 true,因此如果您在之前的pointerInput 中使用它们,它们将永远不会收到任何事件。PointeInputChange.positionChange() PointerInputChange.isConsumed

例如拖动源代码

suspend fun PointerInputScope.detectDragGestures(
    onDragStart: (Offset) -> Unit = { },
    onDragEnd: () -> Unit = { },
    onDragCancel: () -> Unit = { },
    onDrag: (change: PointerInputChange, dragAmount: Offset) -> Unit
) {
    forEachGesture {
        awaitPointerEventScope {
            val down = awaitFirstDown(requireUnconsumed = false)
            var drag: PointerInputChange?
            var overSlop = Offset.Zero
            do {
                drag = awaitPointerSlopOrCancellation(
                    down.id,
                    down.type
                ) { change, over ->
                    change.consume()
                    overSlop = over
                }
            // ! EVERY Default GESTURE HAS THIS CHECK
            } while (drag != null && !drag.isConsumed)
            if (drag != null) {
                onDragStart.invoke(drag.position)
                onDrag(drag, overSlop)
                if (
                    !drag(drag.id) {
                        onDrag(it, it.positionChange())
                        it.consume()
                    }
                ) {
                    onDragCancel()
                } else {
                    onDragEnd()
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以链接 Modifier.pointerInput() 而不是使用 Modifier.run

Modifier
  .pointerInput(keys){
    // Gesture scope1
    if(zoomEnabled){...}
  } 
  .pointerInput(keys){
    // Gesture scope2
      if(panEnabled){
        ....
    }

  } 
Run Code Online (Sandbox Code Playgroud)

事件首先进入手势范围 2,然后进入手势范围 1

创建了一个小示例,您可以观察手势如何变化和传播以及它们如何使用按键重置

@Composable
private fun MyComposable() {

    var zoomEnabled by remember { mutableStateOf(false) }
    var dragEnabled by remember { mutableStateOf(false) }
    var text by remember { mutableStateOf("") }

    Column() {
        val modifier = Modifier
            .size(400.dp)
            .background(Color.Red)
            .pointerInput(zoomEnabled) {
              if (zoomEnabled) {
                  detectTransformGestures { centroid, pan, zoom, rotation ->
                      println("ZOOOMING")
                      text = "ZOOMING centroid: $centroid"
                  }
              }
            }
            .pointerInput(key1 = dragEnabled, key2= zoomEnabled) {
                if (dragEnabled && !zoomEnabled) {
                    detectDragGestures { change, dragAmount ->
                        println("DRAGGING")
                        text = "DRAGGING $dragAmount"
                    }
                }
            }
        Box(modifier = modifier)

        Text(text = text)

        OutlinedButton(onClick = { zoomEnabled = !zoomEnabled }) {
            Text("zoomEnabled: $zoomEnabled")
        }

        OutlinedButton(onClick = { dragEnabled = !dragEnabled }) {
            Text("dragEnabled: $dragEnabled")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以使用上面的答案和片段创建自己的行为。