# 如何解决多层级组件手势冲突

#### 问题现象

多层级手势事件指父子组件嵌套时，父子组件均绑定了手势或事件。在该场景下，手势或者事件的响应受到多个因素的影响，相互之间发生传递和竞争，容易出现预期外的响应。对于多层级手势使用一般有下面几种问题场景：

| 常见问题场景 | 问题简述 |
| --- | --- |
| 场景一 | Stack组件设置并行手势导致手势冲突 |
| 场景二 | 如何阻止onTouch事件从子组件传向父组件 |
| 场景三 | 如何隔离两个自定义控件的触摸事件 |
| 场景四 | 手势如何从上层组件向下层组件透传 |

#### 场景一

Stack组件绑定并行手势，触发Stack长按手势情况下，如何使Stack内子组件点击事件不触发？

问题代码如下：

```
@Entry
@Component
struct StackExample {
  @State count: number = 0;

  build() {
    Stack({ alignContent: Alignment.Bottom }) {
      Text('First child, show in bottom')
        .width('90%')
        .height('100%')
        .backgroundColor('#f1f3f5')
        .textAlign(TextAlign.Center)
        .borderRadius(10)
        .gesture(
          LongPressGesture()
            .onAction(() => {
              console.info('text 点击');
            })
        );
    }.width('100%').height(150).margin({ top: 5 })
    .parallelGesture(
      LongPressGesture()
        .onAction(() => {
          console.info('stack 长按');
        }), GestureMask.Normal);
  }
}
```

#### 场景二

PanGesture绑定在父组件上，子组件有绑定onTouch，如何在子组件onTouch的时候，阻止父组件响应PanGesture？

#### 场景三

如何隔离两个自定义控件的触摸事件？

#### 场景四

界面覆盖一个透明度为0的Stack组件后，下层组件不再响应手势事件？如何实现下层组件手势响应？

#### 背景知识

*   parallelGesture：绑定可与子组件手势同时触发的手势。手势事件为非冒泡事件。父组件设置parallelGesture时，父子组件相同的手势事件都可以触发，实现类似冒泡效果。
*   事件冒泡：基础事件在响应链上的传递遵循冒泡机制，即最内层组件优先处理，再逐层往父组件传递该事件，任意一层组件可主动终止本次事件的继续传递，即终止冒泡。但需要注意的是，终止冒泡并不会中断父组件对手势的响应处理。
*   组合手势：手势识别组合，即两种及以上手势组合为复合手势，支持顺序识别、并发识别和互斥识别。
*   事件独占控制：在一个窗口内，设置了独占控制的组件上的事件如果首先响应，则本次交互只允许此组件上设置的事件响应，窗口内其他组件上的事件不会响应。
*   hitTestBehavior：可以实现在复杂的多层级场景下，一些组件能够响应手势和事件，而一些组件不能响应手势和事件。

#### 解决方案

#### 场景一

由于父组件设置parallelGesture时，父子组件相同的手势事件都可以触发，导致长按Stack会触发子组件点击事件，可以通过给子组件加上长按事件和组件的点击事件做竞争。

示例代码如下：

```
@Entry
@Component
struct StackExample {
  build() {
    Stack({ alignContent: Alignment.Bottom }) {
      Text('First child, show in bottom')
        .width('90%')
        .height('100%')
        .backgroundColor('#f1f3f5')
        .textAlign(TextAlign.Center)
        .borderRadius(10)
        .gesture(
          GestureGroup(GestureMode.Exclusive,
            LongPressGesture()
              .onAction(() => {
                console.info('Text 长按');
              }),
            TapGesture()
              .onAction(() => {
                console.info('Text 点击');
              })
          )
        );
    }.width('100%').height(150).margin({ top: 5 })
    .parallelGesture(
      LongPressGesture()
        .onAction(() => {
          console.info('stack 长按');
        }), GestureMask.Normal);
  }
}
```

效果如图：

#### 场景二

在子组件onTouch事件回调中调用stopPropagation阻断触摸事件向父组件传递。

子组件调用stopPropagation只会阻止父组件响应onTouch事件，无法阻止onClick事件的触发。

示例代码如下

```
@Entry
@Component
struct TouchExample {
  text: string = '';

  build() {
    Column() {
      Button('Touch')
        .height(40)
        .width(100)
        .onTouch((event?: TouchEvent) => {
          console.info('child onTouch');
          event?.stopPropagation();
        });
      Text(this.text);
    }
    .onTouch(() => {
      console.info('father onTouch');
    })
    .onClick(() => {
      console.info('father onClick');
    })
    .width('100%')
    .padding(30);
  }
}
```

效果如图：

#### 场景三

通过配置monopolizeEvents是否独占事件实现隔离两个自定义控件的触摸事件，true表示组件独占事件，false表示组件不独占事件，具体使用示例可参考示例。

#### 场景四

ArkUI事件响应链通过触摸测试进行收集，遵循右子树（按组件布局的先后层级）优先的后序遍历。

hitTestBehavior属性均为默认，自身及子节点响应触摸测试，但阻塞兄弟节点的触摸测试，不影响祖先节点的触摸测试。

如果想实现手势事件从组件上层传递下层，可设置hitTestBehavior为HitTestMode.Transparent，自身和子节点均响应触摸测试，不会阻塞兄弟节点和祖先节点的触摸测试，具体使用可参考示例：hitTestBehavior对手势和事件的控制。