# 3.11:手势设置

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

# 3.11.1:手势类属性定义介绍

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组件时,内置的滑动手势仍然会触发。
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

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

  • gesture:给当前组件绑定手势识别器,子组件优于父组件识别手势。
  • priorityGesture:绑定优先识别手势。默认情况下,子组件优先于父组件识别手势,当父组件配置此方法时,父组件优先于子组件进行识别。
  • parallelGesture:绑定可与子组件手势同时触发的手势。手势事件为非冒泡事件。父组件设置此方法时,父子组件相同的手势事件都可以触发,实现类似冒泡效果。

以上 3 个方法的参数都是一致的,参数 gesture 表示手势识别器, GestureType 提供了以下 7 种手势识别器,

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

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

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

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

# 3.11.2:点击手势TapGesture

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

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

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

    @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)
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49

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

    3_5_2_1

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

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

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

    @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)
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49

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

    3_5_2_2

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

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

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

    @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)
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49

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

    3_5_2_3

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

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

# 3.11.3:长按手势LongPressGesture

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;
}
1
2
3
4
5
6

创建长按手势识别器时可以配置相关参数, fingers 表示手指数量,repeat表示重复识别,duration表示长按时间, onAction 表示长按手势识别的事件回调。onActionEnd表示长按结束时的回调,onActionCancel表示长按取消的回调。

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

    @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)
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62

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

    3_5_3_1

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

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

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

    @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)
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60

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

    3_5_3_2

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

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

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

    @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)
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61

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

    3_5_3_3

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

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

# 3.11.4:平移手势PanGesture

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;
}
1
2
3
4
5
6
7

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

@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)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
3_5_4_1

# 3.11.5:捏合手势PinchGesture

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;
}
1
2
3
4
5
6
7

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

@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)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# 3.11.6:旋转手势RotationGesture

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;
}
1
2
3
4
5
6
7

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

@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)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
(adsbygoogle = window.adsbygoogle || []).push({});
请作者喝杯咖啡

津公网安备 12011402001367号

津ICP备2020008934号-2

中央网信办互联网违法和不良信息举报中心

天津市互联网违法和不良信息举报中心