ArkUI 多层级手势:父子组件别互相抢动作

举报
蓝瘦的蜕变 发表于 2026/10/21 21:32:27 2026/10/21
【摘要】 今天我们看父容器和子组件都绑定手势时,响应边界怎么判断。 新手写手势很容易把动作绑得到处都是:父容器一个 TapGesture,子卡片也一个 TapGesture。页面能跑,但一旦用户点到重叠区域,你就会开始怀疑到底谁该响应。 参考文档: https://developer.huawei.com/consumer/cn

ArkUI 多层级手势:父子组件别互相抢动作

今天我们看父容器和子组件都绑定手势时,响应边界怎么判断。

新手写手势很容易把动作绑得到处都是:父容器一个 TapGesture,子卡片也一个 TapGesture。页面能跑,但一旦用户点到重叠区域,你就会开始怀疑到底谁该响应。

参考文档:

  • https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-interaction-development-guide-overview

父容器也能绑定手势

.gesture(
  TapGesture({ count: 1 })
    .onAction(() => {
      this.parentText = '父容器响应'
    }),
  GestureMask.Normal
)

GestureMask.Normal 是普通识别方式。一般页面先从它开始,不要一上来就强行抢内部手势。

IgnoreInternal 会改变响应优先级

this.useIgnoreInternal ? GestureMask.IgnoreInternal : GestureMask.Normal

这个 Demo 用一个开关切换父容器的 GestureMask。打开后,父容器会更强势地处理手势。我们不是要鼓励你到处用它,而是让你知道:父子组件都有手势时,优先级是可以被显式影响的。

你写列表卡片时,有没有遇到过“点按钮却触发整张卡片点击”的情况?这种问题就适合先从绑定层级查起。

完整示例代码

文件位置:entry/src/main/ets/pages/arkUI/interaction/ArkUIMultiLevelGestureDemo.ets

@Entry
@Component
struct ArkUIMultiLevelGestureDemo {
  @State useIgnoreInternal: boolean = false
  @State parentText: string = '点击外层浅蓝区域,父容器会响应'
  @State childText: string = '点击内层白色卡片,子组件会响应'

  private showTip(message: string): void {
    this.getUIContext().getPromptAction().showToast({
      message: message,
      duration: 1200,
      bottom: 120
    })
  }

  @Builder
  sectionTitle(title: string, desc: string) {
    Column({ space: 6 }) {
      Text(title)
        .fontSize(19)
        .fontWeight(FontWeight.Bold)
        .fontColor('#111827')
      Text(desc)
        .fontSize(13)
        .fontColor('#6B7280')
        .lineHeight(20)
    }
    .width('100%')
    .alignItems(HorizontalAlign.Start)
  }

  build() {
    Scroll() {
      Column({ space: 22 }) {
        Column({ space: 8 }) {
          Text('ArkUI 多层级手势')
            .fontSize(28)
            .fontWeight(FontWeight.Bold)
            .fontColor('#111827')
          Text('父子组件别互相抢动作')
            .fontSize(16)
            .fontColor('#4B5563')
          Text('多层级手势先看清楚一件事:手势到底绑在父容器,还是绑在子组件。这里用 GestureMask.Normal 和 IgnoreInternal 做最小对照。')
            .fontSize(14)
            .fontColor('#6B7280')
            .lineHeight(22)
        }
        .width('100%')
        .alignItems(HorizontalAlign.Start)

        this.sectionTitle('切换父容器的 GestureMask', 'Normal 会按正常规则识别;IgnoreInternal 会让父容器忽略内部手势,优先识别自己的手势。')
        Toggle({ type: ToggleType.Switch, isOn: this.useIgnoreInternal })
          .onChange((isOn: boolean) => {
            this.useIgnoreInternal = isOn
            this.parentText = isOn ? '父容器当前使用 GestureMask.IgnoreInternal' : '父容器当前使用 GestureMask.Normal'
            this.showTip(isOn ? '父容器优先识别' : '恢复普通识别')
          })

        Column({ space: 14 }) {
          Text(this.useIgnoreInternal ? '父容器:IgnoreInternal' : '父容器:Normal')
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .fontColor('#0F172A')
          Text('点外层区域或内层卡片,观察下面两行反馈。')
            .fontSize(13)
            .fontColor('#475569')

          Column({ space: 10 }) {
            Text('子组件卡片')
              .fontSize(18)
              .fontWeight(FontWeight.Bold)
              .fontColor('#111827')
            Text('这里也绑定了 TapGesture')
              .fontSize(13)
              .fontColor('#64748B')
          }
          .width('100%')
          .padding(18)
          .borderRadius(16)
          .backgroundColor('#FFFFFF')
          .border({ width: 1, color: '#CBD5E1' })
          .gesture(
            TapGesture({ count: 1 })
              .onAction(() => {
                this.childText = '子组件响应:内层卡片 TapGesture 被触发'
                this.showTip('子组件响应')
              })
          )
        }
        .width('100%')
        .padding(18)
        .borderRadius(18)
        .backgroundColor('#DBEAFE')
        .border({ width: 1, color: '#93C5FD' })
        .gesture(
          TapGesture({ count: 1 })
            .onAction(() => {
              this.parentText = this.useIgnoreInternal ? '父容器响应:IgnoreInternal 模式下优先识别' : '父容器响应:外层区域被点击'
              this.showTip('父容器响应')
            }),
          this.useIgnoreInternal ? GestureMask.IgnoreInternal : GestureMask.Normal
        )

        Column({ space: 8 }) {
          Text(this.parentText)
            .fontSize(14)
            .fontColor('#111827')
            .lineHeight(22)
          Text(this.childText)
            .fontSize(14)
            .fontColor('#111827')
            .lineHeight(22)
        }
        .width('100%')
        .padding(16)
        .borderRadius(14)
        .backgroundColor('#FFFFFF')
        .border({ width: 1, color: '#E5E7EB' })
      }
      .width('100%')
      .padding(24)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F8FAFC')
  }
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。