处理键盘操作

当用户将焦点放在可编辑文本组件(例如 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,这会停止事件的传播。

以下代码段展示了如何在用户释放 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 键点击事件

Spacebar 和 Enter 键也会触发点击事件。 例如,用户可以通过处理点击事件,使用 Spacebar 或 Enter 键切换(播放或暂停)媒体播放,如下所示:

MediaPlayer(modifier = Modifier.clickable { togglePausePlay() })

clickable 修饰符会拦截按键事件 并在按下 Spacebar 或 Enter 键时调用 onClick() 回调。 因此,在代码段中,按下 Spacebar 或 Enter 键会调用 togglePausePlay() 函数 。

未使用的按键事件

未使用的按键事件会从发生事件的组件传播到封闭的外部组件。 在下面的示例中,InnerComponent 会在使用按键事件 时使用 S 键, 因此 OuterComponent 不会收到因释放 S 键而触发的任何按键事件。 因此,actionB() 函数永远不会被调用。

InnerComponent 上的其他按键事件(例如释放 D 键) 可以由 OuterComponent 处理。 由于释放 D 键的按键事件传播到了 OuterComponent,因此会调用 actionC() 函数。

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 intercepts the key event by returning true.

父组件可以拦截其子组件上发生的按键事件。 在以下代码段中,系统会调用 previewSKey() 函数 当用户按下 S 键时, 而不是调用 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()。 您可以利用此行为来实现屏幕范围内的键盘快捷键。

其他资源