Compose手势

Compose手势

本文链接:

点击
拖动
滑动

锚点

Compose Drag 拖动原理

Compose Drag 拖动原理:
    等待第一次按下 挂起 // UI展现出来的时候,这个while循环就已经在等待第一次按下了。
    事件 -> 恢复
    判断拖动合法性
    合法
        onDragStart
        onDrag
        onDragEnd

forEachGesture{
    awaitPointerEventScope{
        val down = awaitFirstDown()

        onDragStart.invoke
        onDrag
        onDragCancel // 看条件
        onDragEnd // 看条件
    }
}

变换手势原理

功能代码

@Composable
fun TransformGestureDemo() {
    var boxSize = 100.dp
    var offset by remember { mutableStateOf(Offset.Zero) }
    var ratationAngle by remember { mutableStateOf(0f) }
    var scale by remember { mutableStateOf(1f) }
    Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
        Box(Modifier
            .size(boxSize)
            .rotate(ratationAngle) // 需要注意offset与rotate的调用先后顺序
            .scale(scale)
            .offset {
                IntOffset(offset.x.roundToInt(), offset.y.roundToInt())
            }
            .background(Color.Green)
            .pointerInput(Unit) {
                detectTransformGestures(
                    panZoomLock = true, // 平移或放大时是否可以旋转
                    // 该回调,会在内部被调用
                    onGesture = { centroid: Offset, pan: Offset, zoom: Float, rotation: Float ->
                        offset += pan
                        scale *= zoom
                        ratationAngle += rotation
                    }
                )
            }
        )
    }
}

源码

  • UI页面展示的时候,就在等待第一个事件到来(挂起)
suspend fun PointerInputScope.detectTransformGestures(
    panZoomLock: Boolean = false,
    onGesture: (centroid: Offset, pan: Offset, zoom: Float, rotation: Float) -> Unit
) {
    // while循环,处理事件
    forEachGesture {
        awaitPointerEventScope {
            // 省略xxx
            // 等待第一次按下
            awaitFirstDown(requireUnconsumed = false)
            do {
                val event = awaitPointerEvent() // 点击事件
                val canceled = event.changes.fastAny { it.isConsumed }
                if (!canceled) {
                    val zoomChange = event.calculateZoom() // 变换值
                    val rotationChange = event.calculateRotation() // 变换值
                    val panChange = event.calculatePan() // 变换值
                    
                    // 省略xxx

                    // 调用回调(我们自己实现的方法)
                    onGesture(centroid, panChange, zoomChange, effectiveRotation)
                }
            } while (!canceled && event.changes.fastAny { it.pressed })
        }
    }
}

点击事件分发

1、ComposeView装载AndroidComposeView

2、补充知识点:onTouchEvent返回true代表消费完了,从上到下,从下到上 => U型结构自上而下

3、AndroidComposeView.java#dispatchTouchEvent

dispatchTouchEvent()
-->val processResult = handleMotionEvent(motionEvent)
  -->pointerInputEventProcessor.process(pointerInputEvent, xxx)
    -->root.hitTest() // flutter有个方法hitTest,一批人开发,用于点击测试/点击命中
    -->hitPathTracker.addHitPath()// 加入到【候选人名单】中
    -->hitPathTracker.dispatchChanges() // 处理两种事件,三种事件会分发三次
      -->root.dispatchMainEventPass()
        -->children.forEach {dispatched = it.dispatchMainEventPass() || dispatched} // 点击测试,从父节点到子节点
          -->pointerInputNode.onPointerEvent(event, PointerEventPass.Main, size)
            -->dispatchPointerEvent(pointerEvent, pass)
              -->it.offerPointerEvent(pointerEvent, pass)
                -->resume(event)  // 恢复 awaitFirstDown(),第一次按下会挂起。 ==> 上面变换、拖拽、点击的awaitFirstDown()
      -->root.dispatchFinalEventPass()
-->if (processResult.anyMovementConsumed) parent.requestDisallowInterceptTouchEvent(true)
-->return processResult.dispatchedToAPointerInputModifier

Compose事件

1、事件传递:从上至下

  1. root
  2. layoutNode1
  3. layoutNode2
  4. layoutNode3

2、候选人机制:

  1. 3个Node都包含点击的这个点
  2. Compose会把包含了这个点的,都加入到候选人名单(共四个)
  3. 不想走两遍

3、对名单中候选人进行判定:不能处理的就从名单剔除 ====> Flutter也是如此

  1. 从layoutNode3(最上层的)优先判定

4、添加到候选人名单的条件(名单一般很少,几个)

  1. 包含点
  2. 添加了事件

5、Compose事件类型,会分发两个线路main和final

  1. init
  2. main
  3. final

6、Root是什么?

  1. LayoutNode
  2. 根节点
AndroidComposeView.java对root初始化:
    override val root = LayoutNode().also {
        it.measurePolicy = RootMeasurePolicy
        it.density = density
        // Composed modifiers cannot be added here directly
        it.modifier = Modifier
            .then(semanticsModifier)
            .then(rotaryInputModifier)
            .then(_focusManager.modifier)
            .then(keyInputModifier)
    }

你可能感兴趣的:(Android,开发语言)