1# 组合手势
2
3
4组合手势由多种单一手势组合而成,通过在GestureGroup中使用不同的[GestureMode](../reference/apis-arkui/arkui-ts/ts-combined-gestures.md#gesturemode枚举说明)来声明该组合手势的类型,支持[顺序识别](#顺序识别)、[并行识别](#并行识别)和[互斥识别](#互斥识别)三种类型。
5
6```ts
7GestureGroup(mode:GestureMode, gesture:GestureType[])
8```
9
10
11- mode:为GestureMode枚举类。用于声明该组合手势的类型。
12
13- gesture:由多个手势组合而成的数组。用于声明组合成该组合手势的各个手势。
14
15
16## 顺序识别
17
18顺序识别组合手势对应的GestureMode为Sequence。顺序识别组合手势将按照手势的注册顺序识别手势,直到所有的手势识别成功。当顺序识别组合手势中有一个手势识别失败时,后续手势识别均失败。顺序识别手势组仅有最后一个手势可以响应onActionEnd。
19
20以一个由长按手势和拖动手势组合而成的连续手势为例:
21
22在一个Column组件上绑定了translate属性,通过修改该属性可以设置组件的位置移动。然后在该组件上绑定LongPressGesture和PanGesture组合而成的Sequence组合手势。当触发LongPressGesture时,更新显示的数字。当长按后进行拖动时,根据拖动手势的回调函数,实现组件的拖动。
23
24```ts
25// xxx.ets
26@Entry
27@Component
28struct Index {
29  @State offsetX: number = 0;
30  @State offsetY: number = 0;
31  @State count: number = 0;
32  @State positionX: number = 0;
33  @State positionY: number = 0;
34  @State borderStyles: BorderStyle = BorderStyle.Solid
35
36  build() {
37    Column() {
38      Text('sequence gesture\n' + 'LongPress onAction:' + this.count + '\nPanGesture offset:\nX: ' + this.offsetX + '\n' + 'Y: ' + this.offsetY)
39        .fontSize(28)
40    }.margin(10)
41    .borderWidth(1)
42    // 绑定translate属性可以实现组件的位置移动
43    .translate({ x: this.offsetX, y: this.offsetY, z: 0 })
44    .height(250)
45    .width(300)
46    //以下组合手势为顺序识别,当长按手势事件未正常触发时不会触发拖动手势事件
47    .gesture(
48      // 声明该组合手势的类型为Sequence类型
49      GestureGroup(GestureMode.Sequence,
50        // 该组合手势第一个触发的手势为长按手势,且长按手势可多次响应
51        LongPressGesture({ repeat: true })
52          // 当长按手势识别成功,增加Text组件上显示的count次数
53          .onAction((event: GestureEvent|undefined) => {
54            if(event){
55              if (event.repeat) {
56                this.count++;
57              }
58            }
59            console.info('LongPress onAction');
60          })
61          .onActionEnd(() => {
62            console.info('LongPress end');
63          }),
64        // 当长按之后进行拖动,PanGesture手势被触发
65        PanGesture()
66          .onActionStart(() => {
67            this.borderStyles = BorderStyle.Dashed;
68            console.info('pan start');
69          })
70            // 当该手势被触发时,根据回调获得拖动的距离,修改该组件的位移距离从而实现组件的移动
71          .onActionUpdate((event: GestureEvent|undefined) => {
72            if(event){
73              this.offsetX = (this.positionX + event.offsetX);
74              this.offsetY = this.positionY + event.offsetY;
75            }
76            console.info('pan update');
77          })
78          .onActionEnd(() => {
79            this.positionX = this.offsetX;
80            this.positionY = this.offsetY;
81            this.borderStyles = BorderStyle.Solid;
82          })
83      )
84      .onCancel(() => {
85        console.log("sequence gesture canceled")
86      })
87    )
88  }
89}
90```
91
92
93![sequence](figures/sequence.gif)
94
95
96>**说明:**
97>
98>拖拽事件是一种典型的顺序识别组合手势事件,由长按手势事件和滑动手势事件组合而成。只有先长按达到长按手势事件预设置的时间后进行滑动才会触发拖拽事件。如果长按事件未达到或者长按后未进行滑动,拖拽事件均识别失败。
99
100
101## 并行识别
102
103并行识别组合手势对应的GestureMode为Parallel。并行识别组合手势中注册的手势将同时进行识别,直到所有手势识别结束。并行识别手势组合中的手势进行识别时互不影响。
104
105以在一个Column组件上绑定点击手势和双击手势组成的并行识别手势为例,由于单击手势和双击手势是并行识别,因此两个手势可以同时进行识别,二者互不干涉。
106
107```ts
108// xxx.ets
109@Entry
110@Component
111struct Index {
112  @State count1: number = 0;
113  @State count2: number = 0;
114
115  build() {
116    Column() {
117      Text('Parallel gesture\n' + 'tapGesture count is 1:' + this.count1 + '\ntapGesture count is 2:' + this.count2 + '\n')
118        .fontSize(28)
119    }
120    .height(200)
121    .width('100%')
122    // 以下组合手势为并行并别,单击手势识别成功后,若在规定时间内再次点击,双击手势也会识别成功
123    .gesture(
124      GestureGroup(GestureMode.Parallel,
125        TapGesture({ count: 1 })
126          .onAction(() => {
127            this.count1++;
128          }),
129        TapGesture({ count: 2 })
130          .onAction(() => {
131            this.count2++;
132          })
133      )
134    )
135  }
136}
137```
138
139
140![parallel](figures/parallel.gif)
141
142
143>**说明:**
144>
145>当由单击手势和双击手势组成一个并行识别组合手势后,在区域内进行点击时,单击手势和双击手势将同时进行识别。
146>
147>当只有单次点击时,单击手势识别成功,双击手势识别失败。
148>
149>当有两次点击时,若两次点击相距时间在规定时间内(默认规定时间为300毫秒),触发两次单击事件和一次双击事件。
150>
151>当有两次点击时,若两次点击相距时间超出规定时间,触发两次单击事件不触发双击事件。
152
153
154## 互斥识别
155
156互斥识别组合手势对应的GestureMode为Exclusive。互斥识别组合手势中注册的手势将同时进行识别,若有一个手势识别成功,则结束手势识别,其他所有手势识别失败。
157
158以在一个Column组件上绑定单击手势和双击手势组合而成的互斥识别组合手势为例。若先绑定单击手势后绑定双击手势,由于单击手势只需要一次点击即可触发而双击手势需要两次,每次的点击事件均被单击手势消费而不能积累成双击手势,所以双击手势无法触发。若先绑定双击手势后绑定单击手势,则触发双击手势不触发单击手势。
159
160```ts
161// xxx.ets
162@Entry
163@Component
164struct Index {
165  @State count1: number = 0;
166  @State count2: number = 0;
167
168  build() {
169    Column() {
170      Text('Exclusive gesture\n' + 'tapGesture count is 1:' + this.count1 + '\ntapGesture count is 2:' + this.count2 + '\n')
171        .fontSize(28)
172    }
173    .height(200)
174    .width('100%')
175    //以下组合手势为互斥并别,单击手势识别成功后,双击手势会识别失败
176    .gesture(
177      GestureGroup(GestureMode.Exclusive,
178        TapGesture({ count: 1 })
179          .onAction(() => {
180            this.count1++;
181          }),
182        TapGesture({ count: 2 })
183          .onAction(() => {
184            this.count2++;
185          })
186      )
187    )
188  }
189}
190```
191
192
193![exclusive](figures/exclusive.gif)
194
195
196>**说明:**
197>
198>当由单击手势和双击手势组成一个互斥识别组合手势后,在区域内进行点击时,单击手势和双击手势将同时进行识别。
199>
200>当只有单次点击时,单击手势识别成功,双击手势识别失败。
201>
202>当有两次点击时,手势响应取决于绑定手势的顺序。若先绑定单击手势后绑定双击手势,单击手势在第一次点击时即宣告识别成功,此时双击手势已经失败。即使在规定时间内进行了第二次点击,双击手势事件也不会进行响应,此时会触发单击手势事件的第二次识别成功。若先绑定双击手势后绑定单击手势,则会响应双击手势不响应单击手势。
203