处理键盘操作

当用户将焦点放在可编辑的文本组件(例如 TextField)上时, 并且设备连接了硬件键盘, 所有输入均由系统处理 您可以通过处理按键事件来提供键盘快捷键。

默认键盘快捷键

以下键盘快捷键可供直接使用。

键盘快捷键 操作 支持快捷键的可组合项
Shift + Ctrl + 向左箭头/向右箭头 选择直到单词开头/结尾的文字 BasicTextFieldTextField
Shift + Ctrl + 向上箭头/向下箭头 选择段落开头/结尾处的文本 BasicTextFieldTextField
Shift+Alt+向上键/向下键,或 Shift+Meta+向左键/向右键 选择文本到文本开头/结尾处的文本 BasicTextFieldTextField
Shift + 向左箭头/向右箭头 选择角色 BasicTextFieldTextField
Ctrl+A 全选 BasicTextFieldTextField
Ctrl+C/Ctrl+X/Ctrl+V 复制/剪切/粘贴 BasicTextFieldTextField
Ctrl+Z/Ctrl+Shift+Z 撤消/重做 BasicTextFieldTextField
PageDown/PageUp 滚动 LazyColumnverticalScroll 修饰符、scrollable 修饰符

重要事件

在 Compose 中,您可以使用 onKeyEvent 修饰符。 该修饰符接受一个 lambda,该 lambda 会在修改后的组件收到按键事件时被调用。关键事件描述为 KeyEvent 对象。 您可以通过引用传递给 onKeyEvent 修饰符的 lambda 中的对象来获取每个按键事件的信息。

按键操作会发送两个按键事件。其中一个在用户按下按键时触发;另一个在用户松开按键时触发。您可以通过引用 KeyEvent 对象的 type 属性来区分这两种关键事件。

onKeyEvent lambda 的返回值表示 关键事件是否得到处理。 如果您的应用处理按键事件,请返回 true,这会停止事件传播。

以下代码段展示了如何调用 doSomething() 函数 当用户释放 Box 组件上的 S 键时触发:

Box(
    modifier = Modifier.focusable().onKeyEvent {
        if(
            it.type == KeyEventType.KeyUp &&
            it.key == Key.S
        ) {
            doSomething()
            true
        } else {
            false
        }
    }
)  {
    Text("Press S key")
}

辅助键

KeyEvent 对象具有以下属性,用于指示是否按下了修饰键:

具体描述您的应用处理的关键事件。 以下代码段调用 doSomething() 函数 仅在用户仅释放 S 键时才启用。 如果用户按下任何修饰符键(例如 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 修饰符会拦截按键事件,并在按下 SpacebarEnter 键时调用 onClick() 回调。因此,在代码段中按 SpacebarEnter 键即可调用 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
        }
    }
)

默认情况下,每当用户按下 Tab 键时,TextField 组件都会添加一个 Tab 字符,即使使用 onKeyEvent 修饰符处理按键事件也是如此。如需在不添加任何 Tab 字符的情况下移动键盘焦点,请先处理按键事件,然后再触发与按键事件关联的操作,如代码段所示。onKeyPreviewEvent() lambda 会拦截按键事件 返回 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()。您可以通过利用此行为来实现整个屏幕的键盘快捷键。

其他资源