1# 键鼠事件 2 3 4键鼠事件指键盘,鼠标外接设备的输入事件。 5 6 7## 鼠标事件 8 9支持的鼠标事件包含通过外设鼠标、触控板触发的事件。 10 11鼠标事件可触发以下回调: 12 13| 名称 | 描述 | 14| ---------------------------------------- | ---------------------------------------- | 15| onHover(event: (isHover: boolean) => void) | 鼠标进入或退出组件时触发该回调。<br/>isHover:表示鼠标是否悬浮在组件上,鼠标进入时为true, 退出时为false。 | 16| onMouse(event: (event?: MouseEvent) => void) | 当前组件被鼠标按键点击时或者鼠标在组件上悬浮移动时,触发该回调,event返回值包含触发事件时的时间戳、鼠标按键、动作、鼠标位置在整个屏幕上的坐标和相对于当前组件的坐标。 | 17 18当组件绑定onHover回调时,可以通过[hoverEffect](../reference/apis-arkui/arkui-ts/ts-universal-attributes-hover-effect.md#hovereffect)属性设置该组件的鼠标悬浮态显示效果。 19 20 21 **图1** 鼠标事件数据流 22 23 24 25 26 27鼠标事件传递到ArkUI之后,会先判断鼠标事件是否是左键的按下/抬起/移动,然后做出不同响应: 28 29 30- 是:鼠标事件先转换成相同位置的触摸事件,执行触摸事件的碰撞测试、手势判断和回调响应。接着去执行鼠标事件的碰撞测试和回调响应。 31 32- 否:事件仅用于执行鼠标事件的碰撞测试和回调响应。 33 34 35>**说明:** 36> 37>所有单指可响应的触摸事件/手势事件,均可通过鼠标左键来操作和响应。例如当我们需要开发单击Button跳转页面的功能、且需要支持手指点击和鼠标左键点击,那么只绑定一个点击事件(onClick)就可以实现该效果。若需要针对手指和鼠标左键的点击实现不一样的效果,可以在onClick回调中,使用回调参数中的source字段即可判断出当前触发事件的来源是手指还是鼠标。 38 39 40### onHover 41 42 43```ts 44onHover(event: (isHover: boolean) => void) 45``` 46 47 48鼠标悬浮事件回调。参数isHover类型为boolean,表示鼠标进入组件或离开组件。该事件不支持自定义冒泡设置,默认父子冒泡。 49 50 51若组件绑定了该接口,当鼠标指针从组件外部进入到该组件的瞬间会触发事件回调,参数isHover等于true;鼠标指针离开组件的瞬间也会触发该事件回调,参数isHover等于false。 52 53 54>**说明:** 55> 56>事件冒泡:在一个树形结构中,当子节点处理完一个事件后,再将该事件交给它的父节点处理。 57 58 59 60 61```ts 62// xxx.ets 63@Entry 64@Component 65struct MouseExample { 66 @State hoverText: string = 'Not Hover'; 67 @State Color: Color = Color.Gray; 68 69 build() { 70 Column() { 71 Button(this.hoverText) 72 .width(200).height(100) 73 .backgroundColor(this.Color) 74 .onHover((isHover?: boolean) => { // 使用onHover接口监听鼠标是否悬浮在Button组件上 75 if (isHover) { 76 this.hoverText = 'Hovered!'; 77 this.Color = Color.Green; 78 } 79 else { 80 this.hoverText = 'Not Hover'; 81 this.Color = Color.Gray; 82 } 83 }) 84 }.width('100%').height('100%').justifyContent(FlexAlign.Center) 85 } 86} 87``` 88 89 90该示例创建了一个Button组件,初始背景色为灰色,内容为“Not Hover”。示例中的Button组件绑定了onHover回调,在该回调中将this.isHovered变量置为回调参数:isHover。 91 92 93当鼠标从Button外移动到Button内的瞬间,回调响应,isHover值等于true,isHovered的值变为true,将组件的背景色改成Color.Green,内容变为“Hovered!”。 94 95 96当鼠标从Button内移动到Button外的瞬间,回调响应,isHover值等于false,又将组件变成了初始的样式。 97 98 99 100 101 102### onMouse 103 104 105```ts 106onMouse(event: (event?: MouseEvent) => void) 107``` 108 109 110鼠标事件回调。绑定该API的组件每当鼠标指针在该组件内产生行为(MouseAction)时,触发事件回调,参数为[MouseEvent](../reference/apis-arkui/arkui-ts/ts-universal-mouse-key.md#mouseevent对象说明)对象,表示触发此次的鼠标事件。该事件支持自定义冒泡设置,默认父子冒泡。常用于开发者自定义的鼠标行为逻辑处理。 111 112 113开发者可以通过回调中的MouseEvent对象获取触发事件的坐标(displayX/displayY/windowX/windowY/x/y)、按键([MouseButton](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#mousebutton8))、行为([MouseAction](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#mouseaction8))、时间戳(timestamp)、交互组件的区域([EventTarget](../reference/apis-arkui/arkui-ts/ts-universal-events-click.md#eventtarget8对象说明))、事件来源([SourceType](../reference/apis-arkui/arkui-ts/ts-gesture-settings.md#sourcetype枚举说明))等。MouseEvent的回调函数stopPropagation用于设置当前事件是否阻止冒泡。 114 115 116>**说明:** 117> 118>按键(MouseButton)的值:Left/Right/Middle/Back/Forward 均对应鼠标上的实体按键,当这些按键被按下或松开时触发这些按键的事件。None表示无按键,会出现在鼠标没有按键按下或松开的状态下,移动鼠标所触发的事件中。 119 120 121 122```ts 123// xxx.ets 124@Entry 125@Component 126struct MouseExample { 127 @State buttonText: string = ''; 128 @State columnText: string = ''; 129 @State hoverText: string = 'Not Hover'; 130 @State Color: Color = Color.Gray; 131 132 build() { 133 Column() { 134 Button(this.hoverText) 135 .width(200) 136 .height(100) 137 .backgroundColor(this.Color) 138 .onHover((isHover?: boolean) => { 139 if (isHover) { 140 this.hoverText = 'Hovered!'; 141 this.Color = Color.Green; 142 } 143 else { 144 this.hoverText = 'Not Hover'; 145 this.Color = Color.Gray; 146 } 147 }) 148 .onMouse((event?: MouseEvent) => { // 设置Button的onMouse回调 149 if (event) { 150 this.buttonText = 'Button onMouse:\n' + '' + 151 'button = ' + event.button + '\n' + 152 'action = ' + event.action + '\n' + 153 'x,y = (' + event.x + ',' + event.y + ')' + '\n' + 154 'windowXY=(' + event.windowX + ',' + event.windowY + ')'; 155 } 156 }) 157 Divider() 158 Text(this.buttonText).fontColor(Color.Green) 159 Divider() 160 Text(this.columnText).fontColor(Color.Red) 161 } 162 .width('100%') 163 .height('100%') 164 .justifyContent(FlexAlign.Center) 165 .borderWidth(2) 166 .borderColor(Color.Red) 167 .onMouse((event?: MouseEvent) => { // Set the onMouse callback for the column. 168 if (event) { 169 this.columnText = 'Column onMouse:\n' + '' + 170 'button = ' + event.button + '\n' + 171 'action = ' + event.action + '\n' + 172 'x,y = (' + event.x + ',' + event.y + ')' + '\n' + 173 'windowXY=(' + event.windowX + ',' + event.windowY + ')'; 174 } 175 }) 176 } 177} 178``` 179 180 181在onHover示例的基础上,给Button绑定onMouse接口。在回调中,打印出鼠标事件的button/action等回调参数值。同时,在外层的Column容器上,也做相同的设置。整个过程可以分为以下两个动作: 182 183 1841. 移动鼠标:当鼠标从Button外部移入Button的过程中,仅触发了Column的onMouse回调;当鼠标移入到Button内部后,由于onMouse事件默认是冒泡的,所以此时会同时响应Column的onMouse回调和Button的onMouse回调。此过程中,由于鼠标仅有移动动作没有点击动作,因此打印信息中的button均为0(MouseButton.None的枚举值)、action均为3(MouseAction.Move的枚举值)。 185 1862. 点击鼠标:鼠标进入Button后进行了2次点击,分别是左键点击和右键点击。 187 左键点击时:button = 1(MouseButton.Left的枚举值),按下时:action = 1(MouseAction.Press的枚举值),抬起时:action = 2(MouseAction.Release的枚举值)。 188 189 右键点击时:button = 2(MouseButton.Right的枚举值),按下时:action = 1(MouseAction.Press的枚举值),抬起时:action = 2(MouseAction.Release的枚举值)。 190 191 192 193 194 195如果需要阻止鼠标事件冒泡,可以通过调用stopPropagation()方法进行设置。 196 197 198 199```ts 200class ish{ 201 isHovered:boolean = false 202 set(val:boolean){ 203 this.isHovered = val; 204 } 205} 206class butf{ 207 buttonText:string = '' 208 set(val:string){ 209 this.buttonText = val 210 } 211} 212@Entry 213@Component 214struct MouseExample { 215 @State isHovered:ish = new ish() 216 build(){ 217 Column(){ 218 Button(this.isHovered ? 'Hovered!' : 'Not Hover') 219 .width(200) 220 .height(100) 221 .backgroundColor(this.isHovered ? Color.Green : Color.Gray) 222 .onHover((isHover?: boolean) => { 223 if(isHover) { 224 let ishset = new ish() 225 ishset.set(isHover) 226 } 227 }) 228 .onMouse((event?: MouseEvent) => { 229 if (event) { 230 if (event.stopPropagation) { 231 event.stopPropagation(); // 在Button的onMouse事件中设置阻止冒泡 232 } 233 let butset = new butf() 234 butset.set('Button onMouse:\n' + '' + 235 'button = ' + event.button + '\n' + 236 'action = ' + event.action + '\n' + 237 'x,y = (' + event.x + ',' + event.y + ')' + '\n' + 238 'windowXY=(' + event.windowX + ',' + event.windowY + ')'); 239 } 240 }) 241 } 242 } 243} 244``` 245 246 247在子组件(Button)的onMouse中,通过回调参数event调用stopPropagation回调方法(如下)即可阻止Button子组件的鼠标事件冒泡到父组件Column上。 248 249 250 251```ts 252event.stopPropagation() 253``` 254 255 256效果是:当鼠标在Button组件上操作时,仅Button的onMouse回调会响应,Column的onMouse回调不会响应。 257 258 259### hoverEffect 260 261 262```ts 263hoverEffect(value: HoverEffect) 264``` 265 266 267鼠标悬浮态效果设置的通用属性。参数类型为HoverEffect,HoverEffect提供的Auto、Scale、Highlight效果均为固定效果,开发者无法自定义设置效果参数。 268 269 270 **表1** HoverEffect说明 271 272| HoverEffect枚举值 | 效果说明 | 273| -------------- | ---------------------------------------- | 274| Auto | 组件默认提供的悬浮态效果,由各组件定义。 | 275| Scale | 动画播放方式,鼠标悬浮时:组件大小从100%放大至105%,鼠标离开时:组件大小从105%缩小至100%。 | 276| Highlight | 动画播放方式,鼠标悬浮时:组件背景色叠加一个5%透明度的白色,视觉效果是组件的原有背景色变暗,鼠标离开时:组件背景色恢复至原有样式。 | 277| None | 禁用悬浮态效果。 | 278 279 280 281```ts 282// xxx.ets 283@Entry 284@Component 285struct HoverExample { 286 build() { 287 Column({ space: 10 }) { 288 Button('Auto') 289 .width(170).height(70) 290 Button('Scale') 291 .width(170).height(70) 292 .hoverEffect(HoverEffect.Scale) 293 Button('Highlight') 294 .width(170).height(70) 295 .hoverEffect(HoverEffect.Highlight) 296 Button('None') 297 .width(170).height(70) 298 .hoverEffect(HoverEffect.None) 299 }.width('100%').height('100%').justifyContent(FlexAlign.Center) 300 } 301} 302``` 303 304 305 306 307 308Button默认的悬浮态效果就是Highlight效果,因此Auto和Highlight的效果一样,Highlight会使背板颜色变暗,Scale会让组件缩放,None会禁用悬浮态效果。 309 310 311## 按键事件 312 313### 按键事件数据流 314 315 316 317 318按键事件由外设键盘等设备触发,经驱动和多模处理转换后发送给当前获焦的窗口,窗口获取到事件后,会尝试分发三次事件。三次分发的优先顺序如下,一旦事件被消费,则跳过后续分发流程。 319 3201. 首先分发给ArkUI框架用于触发获焦组件绑定的onKeyPreIme回调和页面快捷键。 3212. 再向输入法分发,输入法会消费按键用作输入。 3223. 再次将事件发给ArkUI框架,用于响应系统默认Key事件(例如走焦),以及获焦组件绑定的onKeyEvent回调。 323 324因此,当某输入框组件获焦,且打开了输入法,此时大部分按键事件均会被输入法消费。例如字母键会被输入法用来往输入框中输入对应字母字符、方向键会被输入法用来切换选中备选词。如果在此基础上给输入框组件绑定了快捷键,那么快捷键会优先响应事件,事件也不再会被输入法消费。 325 326按键事件到ArkUI框架之后,会先找到完整的父子节点获焦链。从叶子节点到根节点,逐一发送按键事件。 327 328Web组件的KeyEvent流程与上述过程有所不同。对于Web组件,不会在onKeyPreIme返回false时候,去匹配快捷。而是第三次按键派发中,Web对于未消费的KeyEvent会通过ReDispatch重新派发回ArkUI。在ReDispatch中再执行匹配快捷键等操作。 329 330### onKeyEvent & onKeyPreIme 331 332 333```ts 334onKeyEvent(event: (event: KeyEvent) => void): T 335onKeyPreIme(event: Callback<KeyEvent, boolean>): T 336``` 337 338 339上述两种方法的区别仅在于触发的时机(见 [按键事件数据流](#按键事件数据流))。其中onKeyPreIme的返回值决定了该按键事件后续是否会被继续分发给页面快捷键、输入法和onKeyEvent。 340 341 342当绑定方法的组件处于获焦状态下,外设键盘的按键事件会触发该方法,回调参数为[KeyEvent](../reference/apis-arkui/arkui-ts/ts-universal-events-key.md#keyevent对象说明),可由该参数获得当前按键事件的按键行为([KeyType](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#keytype))、键码([keyCode](../reference/apis-input-kit/js-apis-keycode.md#keycode))、按键英文名称(keyText)、事件来源设备类型([KeySource](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#keysource))、事件来源设备id(deviceId)、元键按压状态(metaKey)、时间戳(timestamp)、阻止冒泡设置(stopPropagation)。 343 344 345 346```ts 347// xxx.ets 348@Entry 349@Component 350struct KeyEventExample { 351 @State buttonText: string = ''; 352 @State buttonType: string = ''; 353 @State columnText: string = ''; 354 @State columnType: string = ''; 355 356 build() { 357 Column() { 358 Button('onKeyEvent') 359 .defaultFocus(true) 360 .width(140).height(70) 361 .onKeyEvent((event?: KeyEvent) => { // 给Button设置onKeyEvent事件 362 if(event){ 363 if (event.type === KeyType.Down) { 364 this.buttonType = 'Down'; 365 } 366 if (event.type === KeyType.Up) { 367 this.buttonType = 'Up'; 368 } 369 this.buttonText = 'Button: \n' + 370 'KeyType:' + this.buttonType + '\n' + 371 'KeyCode:' + event.keyCode + '\n' + 372 'KeyText:' + event.keyText; 373 } 374 }) 375 376 Divider() 377 Text(this.buttonText).fontColor(Color.Green) 378 379 Divider() 380 Text(this.columnText).fontColor(Color.Red) 381 }.width('100%').height('100%').justifyContent(FlexAlign.Center) 382 .onKeyEvent((event?: KeyEvent) => { // 给父组件Column设置onKeyEvent事件 383 if(event){ 384 if (event.type === KeyType.Down) { 385 this.columnType = 'Down'; 386 } 387 if (event.type === KeyType.Up) { 388 this.columnType = 'Up'; 389 } 390 this.columnText = 'Column: \n' + 391 'KeyType:' + this.buttonType + '\n' + 392 'KeyCode:' + event.keyCode + '\n' + 393 'KeyText:' + event.keyText; 394 } 395 }) 396 } 397} 398``` 399 400 401上述示例中给组件Button和其父容器Column绑定onKeyEvent。应用打开页面加载后,组件树上第一个可获焦的非容器组件自动获焦,设置Button为当前页面的默认焦点,由于Button是Column的子节点,Button获焦也同时意味着Column获焦。获焦机制见[焦点事件](arkts-common-events-focus-event.md)。 402 403 404 405 406 407打开应用后,依次在键盘上按这些按键:“空格、回车、左Ctrl、左Shift、字母A、字母Z”。 408 409 4101. 由于onKeyEvent事件默认是冒泡的,所以Button和Column的onKeyEvent都可以响应。 411 4122. 每个按键都有2次回调,分别对应KeyType.Down和KeyType.Up,表示按键被按下、然后抬起。 413 414 415如果要阻止冒泡,即仅Button响应键盘事件,Column不响应,在Button的onKeyEvent回调中加入event.stopPropagation()方法即可,如下: 416 417 418 419```ts 420@Entry 421@Component 422struct KeyEventExample { 423 @State buttonText: string = ''; 424 @State buttonType: string = ''; 425 @State columnText: string = ''; 426 @State columnType: string = ''; 427 428 build() { 429 Column() { 430 Button('onKeyEvent') 431 .defaultFocus(true) 432 .width(140).height(70) 433 .onKeyEvent((event?: KeyEvent) => { 434 // 通过stopPropagation阻止事件冒泡 435 if(event){ 436 if(event.stopPropagation){ 437 event.stopPropagation(); 438 } 439 if (event.type === KeyType.Down) { 440 this.buttonType = 'Down'; 441 } 442 if (event.type === KeyType.Up) { 443 this.buttonType = 'Up'; 444 } 445 this.buttonText = 'Button: \n' + 446 'KeyType:' + this.buttonType + '\n' + 447 'KeyCode:' + event.keyCode + '\n' + 448 'KeyText:' + event.keyText; 449 } 450 }) 451 452 Divider() 453 Text(this.buttonText).fontColor(Color.Green) 454 455 Divider() 456 Text(this.columnText).fontColor(Color.Red) 457 }.width('100%').height('100%').justifyContent(FlexAlign.Center) 458 .onKeyEvent((event?: KeyEvent) => { // 给父组件Column设置onKeyEvent事件 459 if(event){ 460 if (event.type === KeyType.Down) { 461 this.columnType = 'Down'; 462 } 463 if (event.type === KeyType.Up) { 464 this.columnType = 'Up'; 465 } 466 this.columnText = 'Column: \n' + 467 'KeyType:' + this.buttonType + '\n' + 468 'KeyCode:' + event.keyCode + '\n' + 469 'KeyText:' + event.keyText; 470 } 471 }) 472 } 473} 474``` 475 476 477 478 479使用OnKeyPreIme屏蔽在输入框中使用方向左键。 480```ts 481import { KeyCode } from '@kit.InputKit'; 482 483@Entry 484@Component 485struct PreImeEventExample { 486 @State buttonText: string = ''; 487 @State buttonType: string = ''; 488 @State columnText: string = ''; 489 @State columnType: string = ''; 490 491 build() { 492 Column() { 493 Search({ 494 placeholder: "Search..." 495 }) 496 .width("80%") 497 .height("40vp") 498 .border({ radius:"20vp" }) 499 .onKeyPreIme((event:KeyEvent) => { 500 if (event.keyCode == KeyCode.KEYCODE_DPAD_LEFT) { 501 return true; 502 } 503 return false; 504 }) 505 } 506 } 507} 508``` 509