Skip to content

公共手势类属性

笔者在第 2 节公共事件类属性中讲述组件的公共事件属性,比如点击事件、触摸事件等,这些事件默认遵循冒泡机制,ArkUI 开发框架提供了 3 种手势识别方法供开发者控制事件的冒泡机制,本节笔者简单介绍一下这三种手势识别器的用法。

手势类属性定义介绍

bash

declare class CommonMethod<T> {
  gesture(gesture: GestureType, mask?: GestureMask): T;
  priorityGesture(gesture: GestureType, mask?: GestureMask): T;
  parallelGesture(gesture: GestureType, mask?: GestureMask): T;
}

declare type GestureType = TapGestureInterface // 点击手势,支持单次点击、多次点击识别。
  | LongPressGestureInterface                  // 长按手势。
  | PanGestureInterface                        // 平移手势。
  | PinchGestureInterface                      // 捏合手势。
  | SwipeGestureInterface                      // 擦除手势。
  | RotationGestureInterface                   // 旋转手势。
  | GestureGroupInterface;                     // 手势识别组,多种手势组合为复合手势,支持连续识别、并行识别和互斥识别。

declare enum GestureMask {
  Normal,                                      // 不屏蔽子组件的手势,按照默认手势识别顺序进行识别。
  IgnoreInternal,                              // 屏蔽子组件的手势,仅当前容器的手势进行识别。注意:子组件上系统内置的手势不会被屏蔽,如子组件为List组件时,内置的滑动手势仍然会触发。
}

CommonMethod 提供了 gesture()priorityGesture()parallelGesture() 3 个绑定手势识别器的方法, 它们区别如下:

  • gesture:给当前组件绑定手势识别器,子组件优于父组件识别手势。

  • priorityGesture:绑定优先识别手势。默认情况下,子组件优先于父组件识别手势,当父组件配置此方法时,父组件优先于子组件进行识别。

  • parallelGesture:绑定可与子组件手势同时触发的手势。手势事件为非冒泡事件。父组件设置此方法时,父子组件相同的手势事件都可以触发,实现类似冒泡效果。

以上 3 个方法的参数都是一致的,参数 gesture 表示手势识别器,

GestureType 提供了以下 7 种手势识别器

  • TapGesture:点击手势,支持单次点击、多次点击识别。

  • LongPressGesture:长按手势。

  • PanGesture:平移手势。

  • PinchGesture:捏合手势。

  • RotationGesture:旋转手势。

  • SwipeGesture:擦除手势。

  • GestureGroup:手势识别组,多种手势组合为复合手势,支持连续识别、并行识别和互斥识别。

手势动作动作
TapGesture点击手势,支持单次点击、多次点击识别
LongPressGesture长按手势
PanGesture平移手势
PinchGesture捏合手势
RotationGesture旋转手势
SwipeGesture擦除手势
GestureGroup手势识别组,多种手势组合为复合手势,支持连续识别、并行识别和互斥识别。

mask 表示设置父子组件对的手势识别顺序, GestureMask 提供了以下 2 种识别顺序:

  • Normal(默认值):不屏蔽子组件的手势,按照默认手势识别顺序进行识别。

  • IgnoreInternal:屏蔽子组件的手势,仅当前父组件的手势进行识别。注意:子组件上系统内置的手势不会被屏蔽,如子组件为 List 组件时,内置的滑动手势仍然会触发。

接下来笔者分别介绍一下以上各种手势识别器的用法

点击手势 TapGesture

bash

interface TapGestureInterface {
  (value?: { count?: number; fingers?: number }): TapGestureInterface;
  onAction(event: (event?: GestureEvent) => void): TapGestureInterface;
}

创建点击手势识别器时可以配置相关参数, count 表示识别次数, fingers 表示手指数量, onAction 表示点击手势识别的事件回调。

  • gesture:给当前组件绑定手势识别器,子组件优于父组件识别手势。
bash

@Entry @Component struct GestureTest {

  @State info: string = "Gesture...";

  build() {
    Column({space: 10}) {

      Text(this.info)
        .width("100%")
        .fontSize(22)
        .textAlign(TextAlign.Center)

      Stack() {
        Text("")
          .width(200)
          .height(100)
          .fontSize(20)
          .id("child")
          .backgroundColor("#bbccaa")
          .gesture( // 子组件设置手势识别
            TapGesture({
              count: 1,
              fingers: 1
            }).onAction((event) => {
              this.info = "TapGesture in children";
              console.log("TapGesture in children")
            }
          ), GestureMask.Normal) // 子组件事件类型为Normal
          .backgroundColor("#bbaacc")
      }
      .width("100%")
      .height(120)
      .id("parent")
      .gesture( // 父组件设置手势识别
        TapGesture({
          count: 1,
          fingers: 1
        }).onAction((event) => {
          this.info = "TapGesture in parent";
          console.log("TapGesture in parent")
        }
      ), GestureMask.IgnoreInternal) // 父组件事件类型为IgnoreInternal
      .backgroundColor("#aabbcc")
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}

样例运行结果如下图所示:

图片

如上所示, idchildparent 的父子组件同时通过 gesture() 方法设置了手势识别器,事件按照正常派发流程派发给子组件。

笔者在测试 gesture() 方法时,无论父组件的识别顺序 GestureMask 设置成 Normal 还是 IgnoreInternal ,结果均不起作用。

  • priorityGesture:绑定优先识别手势,父组件优先于子组件识别手势。
bash

@Entry @Component struct GestureTest {

  @State info: string = "Gesture...";

  build() {
    Column({space: 10}) {

      Text(this.info)
        .width("100%")
        .fontSize(22)
        .textAlign(TextAlign.Center)

      Stack() {
        Text("")
          .width(200)
          .height(100)
          .fontSize(20)
          .id("child")
          .backgroundColor("#bbccaa")
          .gesture(                  // 子组件设置手势识别
            TapGesture({
              count: 1,
              fingers: 1
            }).onAction((event) => {
              this.info = this.info + " | TapGesture in children";
              console.log("TapGesture in children")
            }
          ), GestureMask.Normal)     // 子组件事件类型为Normal
          .backgroundColor("#bbaacc")
      }
      .width("100%")
      .height(120)
      .id("parent")
      .priorityGesture(              // 父组件设置手势识别
        TapGesture({
          count: 1,
          fingers: 1
        }).onAction((event) => {
          this.info = "TapGesture in parent";
          console.log("TapGesture in parent")
        }
      ), GestureMask.IgnoreInternal) // 父组件事件类型为IgnoreInternal
      .backgroundColor("#aabbcc")
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}

样例运行结果如下图所示:

图片

如上所示, idparent 的父组件通过 priorityGesture() 方法设置了手势识别器, idchild 的子组件通过 gesture() 方法设置了手势识别器,事件优先派发给了父组件而不会派发给子组件。

笔者在测试 gesture() 方法时,无论父组件的识别顺序 GestureMask 设置成 Normal 还是 IgnoreInternal ,结果均不起作用。

  • parallelGesture:绑定可与子组件手势同时触发的手势。
bash

@Entry @Component struct GestureTest {

  @State info: string = "Gesture...";

  build() {
    Column({space: 10}) {

      Text(this.info)
        .width("100%")
        .fontSize(22)
        .textAlign(TextAlign.Center)

      Stack() {
        Text("")
          .width(200)
          .height(100)
          .fontSize(20)
          .id("child")
          .backgroundColor("#bbccaa")
          .gesture(                  // 子组件设置手势识别
            TapGesture({
              count: 1,
              fingers: 1
            }).onAction((event) => {
              this.info = this.info + " | in children";
              console.log("TapGesture in children")
            }
          ), GestureMask.Normal)     // 子组件事件类型为Normal
          .backgroundColor("#bbaacc")
      }
      .width("100%")
      .height(120)
      .id("parent")
      .parallelGesture(              // 父组件设置手势识别
        TapGesture({
          count: 1,
          fingers: 1
        }).onAction((event) => {
          this.info = "TapGesture in parent";
          console.log("TapGesture in parent")
        }
      ), GestureMask.IgnoreInternal) // 父组件事件类型为IgnoreInternal
      .backgroundColor("#aabbcc")
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}

样例运行结果如下图所示:

图片

如上所示, idparent 的父组件通过 parallelGesture() 方法设置了手势识别器, idchild 的子组件通过 gesture() 方法设置了手势识别器,事件同时派发给了父组件和子组件。

笔者在测试 gesture() 方法时,无论父组件的识别顺序 GestureMask 设置成 Normal 还是 IgnoreInternal ,结果均不起作用

长按手势 LongPressGesture

  • 语法
bash

interface LongPressGestureInterface {
  (value?: { fingers?: number; repeat?: boolean; duration?: number }): LongPressGestureInterface;
  onAction(event: (event?: GestureEvent) => void): LongPressGestureInterface;
  onActionEnd(event: (event?: GestureEvent) => void): LongPressGestureInterface;
  onActionCancel(event: () => void): LongPressGestureInterface;
}

创建长按手势识别器时可以配置相关参数,

类型说明
fingers表示手指数量
repeat表示重复识别
duration表示长按时间
onAction表示长按手势识别的事件回调
onActionEnd表示长按结束时的回调
onActionCancel表示长按取消的回调
  • gesture:给当前组件绑定手势识别器,子组件优于父组件识别手势。
bash

@Entry @Component struct GestureTest {

  @State info: string = "Gesture...";

  build() {
    Column({space: 10}) {

      Text(this.info)
        .width("100%")
        .fontSize(22)
        .textAlign(TextAlign.Center)

      Stack() {
        Text("")
          .width(200)
          .height(100)
          .fontSize(20)
          .id("child")
          .backgroundColor("#bbaacc")
          .gesture(              // 子组件设置手势识别
            LongPressGesture({
              duration: 800,     // 长按800毫秒
              repeat: false,     // 不重复识别
              fingers: 1         // 单个手指
            })
            .onAction(() => {
              this.info = this.info + " | child:action";
            })
            .onActionEnd(() => {
              this.info = this.info + ", end";
            })
            .onActionCancel(() => {
              this.info = this.info + ", cancel";
            })
          )
      }
      .width("100%")
      .height(120)
      .id("parent")
      .backgroundColor("#aabbcc")
      .gesture(                  // 子组件设置手势识别
        LongPressGesture({
          duration: 800,         // 长按800毫秒
          repeat: false,         // 不重复识别
          fingers: 1             // 单个手指
        })
        .onAction(() => {
          this.info = "parent:action";
        })
        .onActionEnd(() => {
          this.info = this.info + ", end";
        })
        .onActionCancel(() => {
          this.info = this.info + ", cancel";
        }),
      GestureMask.IgnoreInternal)
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}

样例运行结果如下图所示:

图片

如上所示, idchildparent 的父子组件同时通过 gesture() 方法设置了手势识别器,事件按照正常派发流程派发给子组件。

笔者在测试 gesture() 方法时,无论父组件的识别顺序 GestureMask 设置成 Normal 还是 IgnoreInternal ,结果均不起作用。

  • priorityGesture:绑定优先识别手势,父组件优先于子组件识别手势。
bash

@Entry @Component struct GestureTest {

  @State info: string = "Gesture...";

  build() {
    Column({space: 10}) {
      Text(this.info)
        .width("100%")
        .fontSize(22)
        .textAlign(TextAlign.Center)
      Stack() {
        Text("")
          .width(200)
          .height(100)
          .fontSize(20)
          .id("child")
          .backgroundColor("#bbaacc")
          .gesture(              // 子组件设置手势识别
            LongPressGesture({
              duration: 800,     // 长按800毫秒
              repeat: false,     // 不重复识别
              fingers: 1         // 单个手指
            })
            .onAction(() => {
              this.info = this.info + " | child:action";
            })
            .onActionEnd(() => {
              this.info = this.info + ", end";
            })
            .onActionCancel(() => {
              this.info = this.info + ", cancel";
            })
          )
      }
      .width("100%")
      .height(120)
      .id("parent")
      .backgroundColor("#aabbcc")
      .priorityGesture(          // 父组件设置手势识别
        LongPressGesture({
          duration: 800,         // 长按800毫秒
          repeat: false,         // 不重复识别
          fingers: 1             // 单个手指
        })
        .onAction(() => {
          this.info = "parent:action";
        })
        .onActionEnd(() => {
          this.info = this.info + ", end";
        })
        .onActionCancel(() => {
          this.info = this.info + ", cancel";
        }),
      GestureMask.IgnoreInternal)
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}

样例运行结果如下图所示:

图片

如上所示, idparent 的父组件通过 priorityGesture() 方法设置了手势识别器, idchild 的子组件通过 gesture() 方法设置了手势识别器,事件优先派发给了父组件而不会派发给子组件

笔者在测试 gesture() 方法时,无论父组件的识别顺序 GestureMask 设置成 Normal 还是 IgnoreInternal ,结果均不起作用。

  • parallelGesture:绑定可与子组件手势同时触发的手势。
bash

@Entry @Component struct GestureTest {

  @State info: string = "Gesture...";

  build() {
    Column({space: 10}) {
      Text(this.info)
        .width("100%")
        .fontSize(22)
        .textAlign(TextAlign.Center)
      Stack() {
        Text("")
          .width(200)
          .height(100)
          .fontSize(20)
          .id("child")
          .backgroundColor("#bbaacc")
          .gesture(              // 子组件设置手势识别
            LongPressGesture({
              duration: 800,     // 长按800毫秒
              repeat: false,     // 不重复识别
              fingers: 1         // 单个手指
            })
            .onAction(() => {
              this.info = this.info + " | c:action";
            })
            .onActionEnd(() => {
              this.info = this.info + ", cend";
            })
            .onActionCancel(() => {
              this.info = this.info + ", ccancel";
            })
          )
      }
      .width("100%")
      .height(120)
      .id("parent")
      .backgroundColor("#aabbcc")
      .parallelGesture(          // 父组件设置手势识别
        LongPressGesture({
          duration: 800,         // 长按800毫秒
          repeat: false,         // 不重复识别
          fingers: 1             // 单个手指
        })
        .onAction(() => {
          this.info = "p:action";
        })
        .onActionEnd(() => {
          this.info = this.info + ", pend";
        })
        .onActionCancel(() => {
          this.info = this.info + ", pcancel";
        }),
        GestureMask.IgnoreInternal
      )
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}

样例运行结果如下图所示:

图片

如上所示, idparent 的父组件通过 parallelGesture() 方法设置了手势识别器, idchild 的子组件通过 gesture() 方法设置了手势识别器,事件同时派发给了父组件和子组件。

笔者在测试 gesture() 方法时,无论父组件的识别顺序 GestureMask 设置成 Normal 还是 IgnoreInternal ,结果均不起作用。

平移手势 PanGesture

bash

interface PanGestureInterface {
  (value?: { fingers?: number; direction?: PanDirection; distance?: number } | PanGestureOptions): PanGestureInterface;
  onActionStart(event: (event?: GestureEvent) => void): PanGestureInterface;
  onActionUpdate(event: (event?: GestureEvent) => void): PanGestureInterface;
  onActionEnd(event: (event?: GestureEvent) => void): PanGestureInterface;
  onActionCancel(event: () => void): PanGestureInterface;
}

创建平移手势识别器时可以配置相关参数, fingers 表示手指数量, direction 表示平移方向, distance 表示平移距离, onActionXXX 表示平移时的事件回调。

参数说明
fingers表示手指数量
direction表示平移方向
distance表示平移距离
onActionXXXX表示平移时的事件回调
bash

@Entry @Component struct GestureTest {

  @State info: string = "Gesture...";

  build() {
    Column({space: 10}) {
      Text(this.info)
        .width("100%")
        .fontSize(22)
        .textAlign(TextAlign.Center)
      Stack() {
        Text("")
          .width(200)
          .height(100)
          .fontSize(20)
          .id("child")
          .backgroundColor("#bbaacc")
          .gesture(                               // 子组件设置手势识别
            PanGesture({
              fingers: 1,                         // 单个手指
              direction: PanDirection.Horizontal, // 水平方向移动
              distance: 20                        // 平移距离
            })
            .onActionStart(() => {
              this.info = this.info + " | cs";
            })
            .onActionUpdate(() => {
              this.info = this.info + ", cu";
            })
            .onActionEnd(() => {
              this.info = this.info + ", ce";
            })
            .onActionCancel(() => {
              this.info = this.info + ", cc";
            })
          )
      }
      .width("100%")
      .height(120)
      .id("parent")
      .backgroundColor("#aabbcc")
      .gesture(                                   // 父组件设置手势识别
        PanGesture({
          fingers: 1,                             // 单个手指
          direction: PanDirection.Horizontal,     // 水平方向移动
          distance: 20                            // 平移距离
        })
        .onActionStart(() => {
          this.info = this.info + " | cs";
        })
        .onActionUpdate(() => {
          this.info = this.info + ", cu";
        })
        .onActionEnd(() => {
          this.info = this.info + ", ce";

        })
        .onActionCancel(() => {
          this.info = this.info + ", cc";
        }),
        GestureMask.IgnoreInternal
      )
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}

图片

捏合手势 PinchGesture

bash

interface PinchGestureInterface {
  (value?: { fingers?: number; distance?: number }): PinchGestureInterface;
  onActionStart(event: (event?: GestureEvent) => void): PinchGestureInterface;
  onActionUpdate(event: (event?: GestureEvent) => void): PinchGestureInterface;
  onActionEnd(event: (event?: GestureEvent) => void): PinchGestureInterface;
  onActionCancel(event: () => void): PinchGestureInterface;
}

创建平移手势识别器时可以配置相关参数, fingers 表示手指数量, direction 表示平移方向, distance 表示平移距离, onActionXXX 表示平移时的事件回调。

参数说明
fingers表示手指数量
distance表示平移距离
onActionXXXX表示平移时的事件回调
bash

@Entry @Component struct GestureTest {

  @State info: string = "Gesture...";

  build() {
    Column({space: 10}) {
      Text(this.info)
        .width("100%")
        .fontSize(22)
        .textAlign(TextAlign.Center)
      Stack() {
        Text("")
          .width(200)
          .height(100)
          .fontSize(20)
          .id("child")
          .backgroundColor("#bbaacc")
          .gesture(                               // 子组件设置手势识别
            PinchGesture({
              fingers: 1,                         // 2个手指
              distance: 20                        // 捏合距离
            })
            .onActionStart(() => {
              this.info = this.info + " | cs";
            })
            .onActionUpdate(() => {
              this.info = this.info + ", cu";
            })
            .onActionEnd(() => {
              this.info = this.info + ", ce";
            })
            .onActionCancel(() => {
              this.info = this.info + ", cc";
            })
          )
      }
      .width("100%")
      .height(120)
      .id("parent")
      .backgroundColor("#aabbcc")
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}

旋转手势 RotationGesture

bash

interface RotationGestureInterface {
  (value?: { fingers?: number; angle?: number }): RotationGestureInterface;
  onActionStart(event: (event?: GestureEvent) => void): RotationGestureInterface;
  onActionUpdate(event: (event?: GestureEvent) => void): RotationGestureInterface;
  onActionEnd(event: (event?: GestureEvent) => void): RotationGestureInterface;
  onActionCancel(event: () => void): RotationGestureInterface;
}

创建旋转手势识别器时可以配置相关参数, fingers 表示手指数量, angle 表示旋转角度, onActionXXX 表示平移时的事件回调。

bash

@Entry @Component struct GestureTest {

  @State info: string = "Gesture...";

  build() {
    Column({space: 10}) {
      Text(this.info)
        .width("100%")
        .fontSize(22)
        .textAlign(TextAlign.Center)
      Stack() {
        Text("")
          .width(200)
          .height(100)
          .fontSize(20)
          .id("child")
          .backgroundColor("#bbaacc")
          .gesture(                               // 子组件设置手势识别
            RotationGesture({
              fingers: 2,                         // 2个手指
              angle: 20                           // 旋转角度
            })
            .onActionStart(() => {
              this.info = this.info + " | cs";
            })
            .onActionUpdate(() => {
              this.info = this.info + ", cu";
            })
            .onActionEnd(() => {
              this.info = this.info + ", ce";
            })
            .onActionCancel(() => {
              this.info = this.info + ", cc";
            })
          )
      }
      .width("100%")
      .height(120)
      .id("parent")
      .backgroundColor("#aabbcc")
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}