1# 自定义手势判定 2<!--Kit: ArkUI--> 3<!--Subsystem: ArkUI--> 4<!--Owner: @jiangtao92--> 5<!--Designer: @piggyguy--> 6<!--Tester: @songyanhong--> 7<!--Adviser: @HelloCrease--> 8 9为组件提供自定义手势判定能力。开发者可根据需要,在手势识别期间,决定是否响应手势。 10 11> **说明:** 12> 13> 从API version 11开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 14 15 16## onGestureJudgeBegin 17 18onGestureJudgeBegin(callback: (gestureInfo: GestureInfo, event: BaseGestureEvent) => GestureJudgeResult): T 19 20为组件绑定自定义手势判定回调。当手势即将成功时,触发用户定义的回调获取结果。 21 22**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 23 24**系统能力:** SystemCapability.ArkUI.ArkUI.Full 25 26**参数:** 27| 参数名 | 类型 | 必填 | 说明 | 28| ---------- | -------------------------- | ------- | ----------------------------- | 29| callback | (gestureInfo: [GestureInfo](./ts-gesture-common.md#gestureinfo11对象说明), event: [BaseGestureEvent](./ts-gesture-common.md#basegestureevent11对象说明)) => [GestureJudgeResult](./ts-gesture-common.md#gesturejudgeresult12) | 是 | 自定义手势判定回调。当手势即将成功时,触发用户定义的回调获取结果。 | 30 31**返回值:** 32 33| 类型 | 说明 | 34| -------- | -------- | 35| T | 返回当前组件。 | 36 37## BaseEvent<sup>8+</sup> 38 39基础事件类型。 40 41### 属性 42 43**系统能力:** SystemCapability.ArkUI.ArkUI.Full 44 45| 名称 | 类型 | 只读 | 可选 | 说明 | 46| ---------| ---------------------------------------- | ---- | ---- | -----------| 47| target | [EventTarget](ts-universal-events-click.md#eventtarget8对象说明) | 否 | 否 | 触发手势事件的元素对象。<br/>**卡片能力:** 从API version 9开始,该接口支持在ArkTS卡片中使用。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。| 48| timestamp| number | 否 | 否 | 事件时间戳,触发事件时距离系统启动的时间间隔。<br>单位:ns<br/>**卡片能力:** 从API version 9开始,该接口支持在ArkTS卡片中使用。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 | 49| source | [SourceType](ts-gesture-settings.md#sourcetype枚举说明8) | 否 | 否 | 事件输入设备的类型。<br/>**卡片能力:** 从API version 9开始,该接口支持在ArkTS卡片中使用。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 | 50| pressure<sup>9+</sup> | number | 否 | 否 | 按压的压力大小。<br/>默认值:0<br/>取值范围:[0,1],典型值0.913168,压感大小与数值正相关。<br/>**卡片能力:** 从API version 9开始,该接口支持在ArkTS卡片中使用。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 | 51| tiltX<sup>9+</sup> | number | 否 | 否 | 手写笔在设备平面上的投影与设备平面X轴的夹角。<br/>默认值:0<br/>**卡片能力:** 从API version 9开始,该接口支持在ArkTS卡片中使用。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 | 52| tiltY<sup>9+</sup> | number | 否 | 否 |手写笔在设备平面上的投影与设备平面Y轴的夹角。<br/>默认值:0<br/>**卡片能力:** 从API version 9开始,该接口支持在ArkTS卡片中使用。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 | 53| rollAngle<sup>17+</sup> | number | 否 | 是 | 手写笔与设备平面的夹角。<br/>**卡片能力:** 从API version 17开始,该接口支持在ArkTS卡片中使用。<br/>**原子化服务API:** 从API version 17开始,该接口支持在原子化服务中使用。 | 54| sourceTool<sup>9+</sup> | [SourceTool](ts-gesture-settings.md#sourcetool枚举说明9) | 否 | 否 | 事件输入源的类型。<br/>**卡片能力:** 从API version 9开始,该接口支持在ArkTS卡片中使用。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 | 55| axisHorizontal<sup>12+</sup> | number | 否 | 是 | 水平轴值。<br/>默认值:0<br/>**说明:**<br/>当前仅在鼠标滚轮或触控板双指滑动触发的Pan手势,或使用Ctrl+鼠标滚轮触发的Pinch手势中可以获取。<br/>**卡片能力:** 从API version 12开始,该接口支持在ArkTS卡片中使用。<br/>**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。| 56| axisVertical<sup>12+</sup> | number | 否 | 是 | 垂直轴值。<br/>默认值:0<br/>**说明:**<br/>当前仅在鼠标滚轮或触控板双指滑动触发的Pan手势,或使用Ctrl+鼠标滚轮触发的Pinch手势中可以获取。<br/>**卡片能力:** 从API version 12开始,该接口支持在ArkTS卡片中使用。<br/>**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 | 57| deviceId<sup>12+</sup> | number | 否 | 是 | 触发当前事件的输入设备ID。<br/>默认值:0<br />取值范围:[0, +∞)<br/>**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。| 58| targetDisplayId<sup>15+</sup> | number | 否 | 是 | 事件发生的屏幕ID。 <br/>默认值:0<br />取值范围:[0, +∞)<br />**原子化服务API:** 从API version 15开始,该接口支持在原子化服务中使用。 | 59 60### getModifierKeyState<sup>12+</sup> 61 62getModifierKeyState?(keys: Array\<string>): boolean 63 64获取功能键按压状态。报错信息请参考以下错误码。支持功能键'Ctrl'\|'Alt'\|'Shift'。 65 66> **说明:** 67> 68> 此接口不支持在手写笔场景下使用。 69 70**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 71 72**系统能力:** SystemCapability.ArkUI.ArkUI.Full 73 74**参数:** 75 76| 参数名 | 类型 | 必填 | 说明 | 77| ------- | ----------- | ---- | --------------------- | 78| keys | Array\<string> | 是 | 需要查询的功能键。 | 79 80**返回值:** 81 82| 类型 | 说明 | 83| ------- | --------------------------------- | 84| boolean | 功能键的按压状态。true表示功能键被按下,false表示功能键未被按下。| 85 86**错误码** 87 88以下错误码详细介绍请参考[通用错误码](../../errorcode-universal.md)。 89 90| 错误码ID | 错误信息 | 91| ------- | -------- | 92| 401 | Parameter error. Possible causes: 1. Incorrect parameter types. 2. Parameter verification failed. | 93 94### getModifierKeyState<sup>12+</sup> 95 96getModifierKeyState?(keys: Array<string>): boolean 97 98获取功能键按压状态。支持功能键 'Ctrl'\|'Alt'\|'Shift'。此接口不支持在手写笔场景下使用。 99 100**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 101 102**系统能力:** SystemCapability.ArkUI.ArkUI.Full 103 104**参数:** 105 106| 参数名 | 类型 | 必填 | 说明 | 107| ------ | --------------------------------- | ---- | -------------------- | 108| keys | Array<string> | 是 | 功能键列表。 | 109 110**返回值:** 111 112| 类型 | 说明 | 113| -------- | -------- | 114| boolean | 返回功能键按压状态。当功能键均处于按压状态时返回true,否则返回false。 | 115 116**错误码:** 117 118以下错误码详细介绍请参考[通用错误码](../../errorcode-universal.md)。 119 120| 错误码ID | 错误信息 | 121| ------- | -------- | 122| 401 | Parameter error. Possible causes: 1. Incorrect parameter types. 2. Parameter verification failed. | 123 124## 示例 125 126### 示例1(自定义手势判定) 127 128该示例通过配置onGestureJudgeBegin实现了对长按、滑动和拖动手势的自定义判定。 129 130```ts 131// xxx.ets 132@Entry 133@Component 134struct Index { 135 @State message: string = ''; 136 137 build() { 138 Column() { 139 Row({ space: 20 }) { 140 Text(this.message).width(200).height(80).backgroundColor(Color.Pink) 141 .fontSize(25) 142 }.margin(20) 143 } 144 .width('100%') 145 .height(200) 146 .borderWidth(2) 147 .onDragStart(() => { 148 this.message = 'drag' 149 console.log("Drag start.") 150 }) 151 .gesture( 152 TapGesture() 153 .tag("tap1")// 设置点击手势标志 154 .onAction(() => { 155 this.message = 'tap1' 156 }) 157 ) 158 .gesture( 159 LongPressGesture() 160 .tag("longPress1")// 设置长按手势标志 161 .onAction(() => { 162 this.message = 'longPress' 163 }) 164 ) 165 .gesture( 166 SwipeGesture() 167 .tag("swipe1")// 设置滑动手势标志 168 .onAction(() => { 169 this.message = 'swipe1' 170 }) 171 ) 172 .gesture( 173 PanGesture() 174 .tag("pan1")// 设置拖动手势标志 175 .onActionStart(() => { 176 this.message = 'pan1' 177 }) 178 ) 179 .onGestureJudgeBegin((gestureInfo: GestureInfo, event: BaseGestureEvent) => { 180 // 若该手势类型为长按手势,转换为长按手势事件 181 if (gestureInfo.type == GestureControl.GestureType.LONG_PRESS_GESTURE) { 182 let longPressEvent = event as LongPressGestureEvent; 183 console.log("repeat " + longPressEvent.repeat) 184 } 185 // 若该手势类型为滑动手势,转换为滑动手势事件 186 if (gestureInfo.type == GestureControl.GestureType.SWIPE_GESTURE) { 187 let swipeEvent = event as SwipeGestureEvent; 188 console.log("angle " + swipeEvent.angle) 189 } 190 // 若该手势类型为拖动手势,转换为拖动手势事件 191 if (gestureInfo.type == GestureControl.GestureType.PAN_GESTURE) { 192 let panEvent = event as PanGestureEvent; 193 console.log("velocity " + panEvent.velocity) 194 } 195 // 自定义判定标准 196 if (gestureInfo.type == GestureControl.GestureType.DRAG) { 197 // 返回 REJECT 会使拖动手势失败 198 return GestureJudgeResult.REJECT; 199 } else if (gestureInfo.tag == 'longPress1' && event.fingerList.length > 0 && event.fingerList[0].localY < 100) { 200 // 返回 CONTINUE 将保持系统判定。 201 return GestureJudgeResult.CONTINUE; 202 } 203 return GestureJudgeResult.CONTINUE; 204 }) 205 } 206} 207``` 208 209 210### 示例2(自定义区域手势判定) 211 212该示例通过配置onGestureJudgeBegin判定区域决定长按手势和拖拽是否响应。 213 214```ts 215// xxx.ets 216import { PromptAction } from '@kit.ArkUI'; 217 218@Entry 219@Component 220struct Index { 221 scroller: Scroller = new Scroller() 222 promptAction: PromptAction = this.getUIContext().getPromptAction(); 223 224 build() { 225 Scroll(this.scroller) { 226 Column({ space: 8 }) { 227 Text("Drag 上下两层 上层绑定长按,下层绑定拖拽。先长按后平移上半区红色区域只会响应长按,先长按后平移下半区蓝色区域只会响应拖拽").width('100%').fontSize(20).fontColor('0xffdd00') 228 .backgroundColor(0xeeddaa00) 229 Stack({ alignContent: Alignment.Center }) { 230 Column() { 231 // 模拟上半区和下半区 232 Stack().width('200vp').height('100vp').backgroundColor(Color.Red) 233 Stack().width('200vp').height('100vp').backgroundColor(Color.Blue) 234 }.width('200vp').height('200vp') 235 // Stack的下半区是绑定了拖动手势的图像区域。 236 Image($r('sys.media.ohos_app_icon')) 237 .draggable(true) 238 .onDragStart(()=>{ 239 this.promptAction.showToast({ message: "Drag 下半区蓝色区域,Image响应" }) 240 }) 241 .width('200vp').height('200vp') 242 // Stack的上半区是绑定了长按手势的浮动区域。 243 Stack() { 244 } 245 .width('200vp') 246 .height('200vp') 247 .hitTestBehavior(HitTestMode.Transparent) 248 .onGestureJudgeBegin((gestureInfo: GestureInfo, event: BaseGestureEvent) => { 249 // 确定tag标志是否有值 250 if (gestureInfo.tag) { 251 console.log("gestureInfo tag" + gestureInfo.tag.toString()) 252 } 253 console.log("gestureInfo Type " + gestureInfo.type.toString() + " isSystemGesture " + gestureInfo.isSystemGesture); 254 console.log("pressure " + event.pressure + " fingerList.length " + event.fingerList.length 255 + " timeStamp " + event.timestamp + " sourceType " + event.source.toString() + " titleX " + event.tiltX + " titleY " + event.tiltY + " rollAngle " + event.rollAngle + " sourcePool " + event.sourceTool.toString()); 256 // 如果是长按类型手势,判断点击的位置是否在上半区 257 if (gestureInfo.type == GestureControl.GestureType.LONG_PRESS_GESTURE) { 258 if (event.fingerList.length > 0 && event.fingerList[0].localY < 100) { 259 return GestureJudgeResult.CONTINUE 260 } else { 261 return GestureJudgeResult.REJECT 262 } 263 } 264 return GestureJudgeResult.CONTINUE 265 }) 266 .gesture(GestureGroup(GestureMode.Parallel, 267 LongPressGesture() 268 .onAction((event: GestureEvent) => { 269 this.promptAction.showToast({ message: "LongPressGesture 长按上半区 红色区域,红色区域响应" }) 270 }) 271 .tag("tap111") 272 )) 273 274 }.width('100%') 275 }.width('100%') 276 } 277 } 278} 279``` 280 281 282### 示例3(实时监测参与手势的有效触点的数量及其简要信息) 283 284该示例通过配置fingerInfos实时检测参与手势的有效触点数量、各个触点ID及其坐标 285 286```ts 287// xxx.ets 288@Entry 289@Component 290struct GestureDetectorExample { 291 @State message: string = '触摸区域' 292 @State fingerCount: number = 0 293 @State fingerDetails: string = '' 294 295 build() { 296 Column() { 297 // 显示信息区域 298 Column() { 299 Text(this.message) 300 .fontSize(20) 301 .fontWeight(FontWeight.Bold) 302 303 Text(`触点数量: ${this.fingerCount}`) 304 .fontSize(16) 305 .margin({ top: 8 }) 306 307 308 Text(this.fingerDetails) 309 .fontSize(14) 310 .margin({ top: 8 }) 311 } 312 .padding(10) 313 .border({ width: 1, color: Color.Gray }) 314 315 // 手势检测区域 316 Column() 317 .width('90%') 318 .height(200) 319 .margin(20) 320 .border({ width: 2, color: Color.Black }) 321 .gesture( 322 GestureGroup(GestureMode.Exclusive, 323 TapGesture() 324 .onAction(() => { 325 this.message = '单击事件' 326 }), 327 LongPressGesture() 328 .onAction(() => { 329 this.message = '长按事件' 330 }), 331 PanGesture() 332 .onActionStart(() => { 333 this.message = '拖动开始' 334 }) 335 .onActionUpdate(() => { 336 this.message = '拖动中...' 337 }) 338 .onActionEnd(() => { 339 this.message = '拖动结束' 340 this.fingerCount = 0 341 this.fingerDetails = '' 342 }) 343 ) 344 ) 345 .onGestureJudgeBegin((gestureInfo: GestureInfo, event: BaseGestureEvent) => { 346 // 获取 fingerInfos 信息 347 if (event?.fingerInfos) { 348 this.fingerCount = event.fingerInfos.length 349 this.fingerDetails = event.fingerInfos.map(finger => 350 `ID:${finger.id}: (${finger.localX.toFixed(1)}, ${finger.localY.toFixed(1)})` 351 ).join('\n') 352 console.log('触点信息:', JSON.stringify(event.fingerInfos)) 353 } 354 if (this.fingerCount > 2) { 355 return GestureJudgeResult.REJECT 356 } 357 return GestureJudgeResult.CONTINUE 358 }) 359 } 360 .width('100%') 361 .height('100%') 362 .padding(10) 363 } 364} 365 366``` 367 368