Processar ações do teclado

Quando o usuário foca em um componente de texto editável, como um TextField, e o dispositivo tem um teclado de hardware conectado, toda a entrada é processada pelo sistema. É possível fornecer atalhos de teclado processando eventos de tecla.

Atalhos de teclado padrão

Os seguintes atalhos de teclado estão disponíveis por padrão.

Atalho do teclado Ação Combináveis que oferecem suporte ao atalho
Shift+Ctrl+Seta para a esquerda/Seta para a direita Selecionar o texto até o início/fim da palavra BasicTextField, TextField
Shift+Ctrl+Seta para cima/Seta para baixo Selecionar o texto até o início/fim do parágrafo BasicTextField, TextField
Shift+Alt+Seta para cima/Seta para baixo ou Shift+Meta+Seta para a esquerda/Seta para a direita Selecionar o texto até o início/fim do texto BasicTextField, TextField
Shift+Seta para a esquerda/Seta para a direita Selecionar personagens BasicTextField, TextField
Ctrl+A Selecionar tudo BasicTextField, TextField
Ctrl+C/Ctrl+X/Ctrl+V Copiar/recortar/colar BasicTextField, TextField
Ctrl+Z/Ctrl+Shift+Z Desfazer/refazer BasicTextField, TextField
PageDown/PageUp Rolar LazyColumn, o modificador verticalScroll, o modificador scrollable

Eventos principais

No Compose, é possível processar um pressionamento de tecla individual com o modificador onKeyEvent. O modificador aceita uma lambda que é chamada quando o componente modificado recebe um evento de tecla. Um evento principal é descrito como um objeto KeyEvent. Para conferir as informações de cada evento principal, consulte o objeto na lambda transmitida ao modificador onKeyEvent.

Uma ação de pressionar uma tecla envia dois eventos de tecla. Um é acionado quando o usuário pressiona a tecla, e o outro é acionado quando a tecla é solta. É possível distinguir os dois eventos principais consultando o atributo type do objeto KeyEvent.

O valor de retorno da lambda onKeyEvent indica se o evento de tecla foi processado ou não. Retorne true se o app processar o evento de tecla, o que interrompe a propagação do evento.

O snippet a seguir mostra como chamar uma função doSomething() quando o usuário solta a tecla S no componente Box:

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

Teclas modificadoras

Um objeto KeyEvent tem os seguintes atributos que indicam se as teclas modificadoras estão pressionadas ou não:

Seja específico ao descrever os eventos principais que seu app processa. O snippet a seguir chama uma função doSomething() somente se o usuário soltar apenas a tecla S. Se o usuário pressionar qualquer tecla modificadora, como Shift, o app não vai chamar a função.

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")
}

Eventos de clique da barra de espaço e da tecla "Enter"

A barra de espaço e a tecla Enter também acionam eventos de clique. Por exemplo, os usuários podem alternar (reproduzir ou pausar) a reprodução de mídia com a barra de espaço ou a tecla Enter processando eventos de clique da seguinte maneira:

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

O modificador clickable intercepta eventos de tecla e chama o callback onClick() quando a barra de espaço ou a tecla Enter é pressionada. É por isso que a função togglePausePlay() é chamada pressionando a barra de espaço ou a tecla Enter no snippet.

Eventos principais não consumidos

Os eventos principais não consumidos são propagados do componente em que o evento ocorreu para o componente externo que o envolve. No exemplo abaixo, o InnerComponent consome eventos de tecla quando a tecla S é liberada. Portanto, o OuterComponent não recebe nenhum evento de tecla acionado pela liberação da tecla S. É por isso que a função actionB() nunca é chamada.

Outros eventos de tecla em InnerComponent, como soltar a tecla D, podem ser processados pelo OuterComponent. A função actionC() é chamada porque o evento de tecla para soltar a tecla D é propagado para o 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
            }
        }
    )
}

Modificador onKeyPreviewEvent

Em alguns casos de uso, é recomendável interceptar um evento de tecla antes que ele acione a ação padrão. Um exemplo comum é adicionar atalhos personalizados a um TextField. O snippet a seguir permite que os usuários passem para o próximo componente focalizável pressionando a tecla 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
        }
    }
)

Por padrão, o componente TextField adiciona um caractere de tabulação sempre que os usuários pressionam a tecla Tab, mesmo que o evento de tecla seja processado com o modificador onKeyEvent. Para mover o foco do teclado sem adicionar caracteres de tabulação, processe o evento de tecla antes de acionar as ações associadas a ele, como no snippet. A lambda onKeyPreviewEvent() intercepta o evento de tecla retornando true.

O componente pai pode interceptar o evento de tecla que ocorre nos filhos. No snippet a seguir, a função previewSKey() é chamada quando os usuários pressionam a tecla S, em vez de chamar a função 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")
    }
}

A lambda onPreviewKeyEvent() do componente Box também não é acionada quando os usuários pressionam a tecla Tab. A lambda onPreviewKeyEvent() é chamada primeiro no componente pai e, em seguida, onPreviewKeyEvent() no componente filho. É possível implementar atalhos de teclado em toda a tela usando esse comportamento.

Outros recursos