目录

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

问题现象

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

常见问题场景

问题简述

场景一

Stack组件设置并行手势导致手势冲突

场景二

如何阻止onTouch事件从子组件传向父组件

场景三

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

场景四

手势如何从上层组件向下层组件透传

[h2]场景一

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);
  }
}

[h2]场景二

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

[h2]场景三

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

[h2]场景四

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

背景知识

解决方案

[h2]场景一

由于父组件设置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);
  }
}

效果如图:

[h2]场景二

在子组件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);
  }
}

效果如图:

[h2]场景三

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

[h2]场景四

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

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

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