当用户将焦点放在可编辑文本组件(例如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表达式,当修改后的组件接收按键事件时调用该表达式。按键事件被描述为KeyEvent
对象。您可以通过引用传递给onKeyEvent
修饰符的lambda表达式中的对象来获取每个按键事件的信息。
一个按键会发送两个按键事件。一个是在用户按下按键时触发;另一个是在释放按键时触发。您可以通过参考type
KeyEvent
对象的属性来区分这两个按键事件。
onKeyEvent
lambda表达式的返回值指示是否处理了按键事件。如果您的应用程序处理了按键事件,则返回true
,这将停止事件的传播。
以下代码片段显示了如何在用户释放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")
}
空格键和回车键单击事件
空格键和回车键也会触发单击事件。例如,用户可以通过处理单击事件来使用空格键或回车键切换(播放或暂停)媒体播放。
MoviePlayer(
modifier = Modifier.clickable { togglePausePlay() }
)
clickable
修饰符拦截按键事件,并在按下空格键或回车键时调用onClick()
回调。这就是为什么在代码片段中通过按下空格键或回车键调用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键时,会调用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()
。您可以通过利用此行为来实现全屏键盘快捷键。
其他资源
- 键盘快捷键助手:允许用户搜索您的应用程序提供的键盘快捷键的系统屏幕。