当用户将焦点放到可编辑文本组件(例如 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.focusable().onKeyEvent {
if(
it.type == KeyEventType.KeyUp &&
it.key == Key.S
) {
doSomething()
true
} else {
false
}
}
) {
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 键切换媒体播放(播放或暂停):
MoviePlayer(
modifier = Modifier.clickable { togglePausePlay() }
)
clickable
修饰符会拦截按键事件,并在按下 空格键或 Enter 键时调用 onClick()
回调。因此,在代码段中,通过按下 空格键或 Enter 键调用了 togglePausePlay()
函数。
未处理的按键事件
未处理的按键事件会从事件发生的组件传播到外部的封闭组件。在下面的示例中,当释放 S 键时,InnerComponent
会处理按键事件,因此 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
}
}
)
默认情况下,TextField
组件会在用户每次按下 Tab 键时添加一个制表符,即使按键事件已通过 onKeyEvent
修饰符处理。要在不添加任何制表符的情况下移动键盘焦点,请在触发与按键事件相关的操作之前处理按键事件,如代码段所示。onKeyPreviewEvent()
lambda 通过返回 true
拦截按键事件。
当用户按下 S 键时,不会调用 Box
组件的 onPreviewKeyEvent()
函数,而是调用 previewSKey()
函数。
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()
。您可以利用此行为来实现全屏键盘快捷键。
其他资源
- 键盘快捷键助手:系统屏幕,使用户能够搜索您的应用提供的键盘快捷键。