Processar ações do teclado

Quando o usuário dá foco a um componente de texto editável, como um TextField, e o dispositivo tem um teclado físico conectado, toda a entrada é processada pelo sistema. Você pode fornecer atalhos do teclado processando eventos de tecla.

Atalhos de teclado padrão

Os atalhos de teclado a seguir já estão disponíveis.

Atalho do teclado Ação Elementos combináveis com 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 caracteres 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 Rolagem LazyColumn, o modificador verticalScroll, o modificador scrollable

Eventos principais

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

Um pressionamento de tecla envia dois eventos de tecla. Um é acionado quando o usuário pressiona a tecla. o outro é acionado quando a chave é liberada. É possível diferenciar os dois eventos principais consultando o atributo type do objeto KeyEvent.

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

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

Box(
    modifier = Modifier.focusable().onKeyEvent {
        if(
            it.type == KeyEventType.KeyUp &&
            it.key == Key.S
        ) {
            doSomething()
            true
        } else {
            false
        }
    }
)  {
    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:

Descreva de maneira específica os eventos principais processados pelo app. 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 a tecla 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 tecla "Enter" e da barra de espaço

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 ao processar eventos de clique da seguinte maneira:

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

O modificador clickable intercepta eventos de tecla e chama o callback onClick() quando a tecla Spacebar ou 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 para o componente onde o evento ocorreu ao componente externo que o contém. No exemplo abaixo, o InnerComponent consome eventos principais quando a chave S é liberada. Assim, o OuterComponent não recebe nenhum evento principal acionado ao liberar a chave S. É por isso que a função actionB() nunca é chamada.

Outros eventos de tecla em InnerComponent, como a liberação da tecla D, podem ser processados pelo OuterComponent. A função actionC() é chamada porque o evento de tecla para liberar 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, você quer interceptar um evento de tecla antes de acionar a ação padrão. Adicionar atalhos personalizados a um TextField é muito comum. O snippet a seguir permite que os usuários passem para o próximo componente com foco 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 acontece nos filhos. No snippet abaixo, 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")
  }
}

O lambda onPreviewKeyEvent() do componente Box também não é acionado quando os usuários pressionam a tecla Tab. A lambda onPreviewKeyEvent() é chamada primeiro no componente pai. então, onPreviewKeyEvent() no componente filho é chamado. Use esse comportamento para implementar atalhos de teclado na tela inteira.

Outros recursos