当用户将焦点置于可编辑的文本组件(例如 TextField)上,并且设备连接了硬件键盘时,所有输入都由系统处理。您可以通过处理按键事件来提供键盘快捷键。
默认键盘快捷键
以下键盘快捷键开箱即用。
| 键盘快捷键 | 操作 | 支持快捷键的可组合项 |
|---|---|---|
| Shift+Ctrl+向左键/向右键 | 选择直到字词开头/结尾的文字 | BasicTextField、TextField |
| Shift+Ctrl+向上键/向下键 | 选择直到段落开头/末尾的文字 | BasicTextField、TextField |
| Shift+Alt+向上键/向下键或 Shift+Meta+向左键/向右键 | 选择直到文本开头/末尾的文字 | BasicTextField、TextField |
| Shift + 向左键/向右键 | 选择角色 | BasicTextField、TextField |
| Ctrl+A | 全选 | BasicTextField、TextField |
| Ctrl+C/Ctrl+X/Ctrl+V | 复制/剪切/粘贴 | BasicTextField、TextField |
| Ctrl+Z/Ctrl+Shift+Z | 撤消/重做 | BasicTextField、TextField |
| PageDown/PageUp | 滚动 | LazyColumn、verticalScroll 修饰符、scrollable 修饰符 |
重要事件
在 Compose 中,您可以使用 onKeyEvent 修饰符处理单个按键。该修饰符接受一个 lambda,当修改后的组件收到按键事件时,系统会调用该 lambda。
关键事件描述为 KeyEvent 对象。您可以通过引用传递给 onKeyEvent 修饰符的 lambda 中的对象来获取每个按键事件的信息。
一次按键会发送两个按键事件。
一个在用户按下按键时触发;另一个在按键松开时触发。
您可以通过引用 KeyEvent 对象的 type 属性来区分这两个关键事件。
onKeyEvent lambda 的返回值指示按键事件是否已处理。
如果您的应用处理了按键事件,则返回 true,这会停止事件的传播。
以下代码段展示了如何在用户松开 Box 组件上的 S 键时调用 doSomething() 函数:
Box( modifier = Modifier .onKeyEvent { if ( it.type == KeyEventType.KeyUp && it.key == Key.S ) { doSomething() true } else { false } } .focusable() ) { Text("Press S key") }
辅助键
KeyEvent 对象具有以下属性,用于指示是否按下了辅助键:
具体说明您的应用处理的关键事件。
以下代码段仅在用户松开 S 键时调用 doSomething() 函数。如果用户按下任何辅助键(例如 Shift 键),应用不会调用该函数。
Box( modifier = Modifier .focusable() .onKeyEvent { if ( it.type == KeyEventType.KeyUp && it.key == Key.S && !it.isAltPressed && !it.isCtrlPressed && !it.isMetaPressed && !it.isShiftPressed ) { doSomething() true } else { false } } ) { Text("Press S key with a modifier key") }
空格键和 Enter 键点击事件
空格键和 Enter 键也会触发点击事件。 例如,用户可以通过处理点击事件,使用空格键或 Enter 键切换(播放或暂停)媒体播放,如下所示:
MediaPlayer(modifier = Modifier.clickable { togglePausePlay() })
clickable 修饰符会拦截按键事件,并在按下空格键或 Enter 键时调用 onClick() 回调。
因此,在代码段中,通过按空格键或 Enter 键来调用 togglePausePlay() 函数。
未使用的按键事件
未使用的按键事件会从发生事件的组件传播到封闭的外部组件。
在以下示例中,InnerComponent 会在 S 键释放时使用按键事件,因此 OuterComponent 不会收到因释放 S 键而触发的任何按键事件。
因此,系统永远不会调用 actionB() 函数。
InnerComponent 上的其他按键事件(例如松开 D 键)可由 OuterComponent 处理。系统会调用 actionC() 函数,因为释放 D 键的按键事件会传播到 OuterComponent。
OuterComponent( modifier = Modifier.onKeyEvent { when { it.type == KeyEventType.KeyUp && it.key == Key.S -> { actionB() // This function is never called. true } it.type == KeyEventType.KeyUp && it.key == Key.D -> { actionC() true } else -> false } } ) { InnerComponent( modifier = Modifier.onKeyEvent { if (it.type == KeyEventType.KeyUp && it.key == Key.S) { actionA() true } else { false } } ) }
onKeyPreviewEvent 修饰符
在某些用例中,您可能希望在按键事件触发默认操作之前拦截该事件。
向 TextField 添加自定义快捷方式就是一种典型做法。
以下代码段可让用户通过按 Tab 键移至下一个可聚焦组件。
val focusManager = LocalFocusManager.current var textFieldValue by remember { mutableStateOf(TextFieldValue()) } TextField( textFieldValue, onValueChange = { textFieldValue = it }, modifier = Modifier.onPreviewKeyEvent { if (it.type == KeyEventType.KeyUp && it.key == Key.Tab) { focusManager.moveFocus(FocusDirection.Next) true } else { false } } )
默认情况下,每次用户按下 Tab 键时,TextField 组件都会添加一个制表符,即使按键事件是使用 onKeyEvent 修饰符处理的也是如此。
如需在不添加任何制表符的情况下移动键盘焦点,请在触发与按键事件关联的操作之前处理按键事件,如代码段中所示。onKeyPreviewEvent() lambda 通过返回 true 来拦截按键事件。
父级组件可以拦截其子级上发生的按键事件。
在以下代码段中,当用户按下 S 键时,系统会调用 previewSKey() 函数,而不是调用 actionForPreview() 函数。
Column( modifier = Modifier.onPreviewKeyEvent { if (it.key == Key.S) { previewSKey() true } else { false } } ) { Box( modifier = Modifier .focusable() .onPreviewKeyEvent { actionForPreview(it) false } .onKeyEvent { actionForKeyEvent(it) true } ) { Text("Press any key") } }
当用户按 Tab 键时,Box 组件的 onPreviewKeyEvent() lambda 也不会触发。系统会先对父级组件调用 onPreviewKeyEvent() lambda,然后对子级组件调用 onPreviewKeyEvent()。您可以利用此行为实现全屏键盘快捷键。
其他资源
- 键盘快捷键辅助工具:一个系统界面,可让用户搜索应用提供的键盘快捷键。