Cómo controlar las acciones del teclado

Cuando el usuario enfoca un componente de texto editable, como un TextField, y el dispositivo tiene un teclado de hardware conectado, el sistema controla toda la entrada. Puedes proporcionar combinaciones de teclas controlando eventos de tecla.

Combinaciones de teclas predeterminadas

Las siguientes combinaciones de teclas están disponibles de inmediato.

Combinación de teclas Acción Componibles que admiten la combinación de teclas
Shift+Ctrl+Flecha hacia la izquierda/Flecha hacia la derecha Seleccionar texto hasta el principio o final de la palabra BasicTextField, TextField
Shift+Ctrl+Flecha arriba/Flecha abajo Seleccionar texto hasta el principio o final del párrafo BasicTextField, TextField
Shift+Alt+Flecha arriba/Flecha abajo o Shift+Meta+Flecha izquierda/Flecha derecha Seleccionar texto hasta el principio o final del texto BasicTextField, TextField
Shift+Flecha hacia la izquierda/Flecha hacia la derecha Seleccionar caracteres BasicTextField, TextField
Ctrl+A Seleccionar todo BasicTextField, TextField
Ctrl+C/Ctrl+X/Ctrl+V Copiar, cortar o pegar BasicTextField, TextField
Ctrl+Z/Ctrl+Shift+Z Deshacer o rehacer BasicTextField, TextField
PageDown/PageUp Desplazamiento LazyColumn, el modificador verticalScroll y el modificador scrollable

Eventos clave

En Compose, puedes controlar una pulsación individual con el onKeyEvent modificador. El modificador acepta una expresión lambda a la que se llama cuando el componente modificado recibe un evento de tecla. Un evento de tecla se describe como un KeyEvent objeto. Puedes obtener la información de cada evento de tecla consultando el objeto en la expresión lambda que se pasa al modificador onKeyEvent.

Una pulsación envía dos eventos de tecla. Uno se activa cuando el usuario presiona la tecla, y el otro, cuando se suelta. Puedes distinguir los dos eventos de tecla consultando el type atributo del objeto KeyEvent.

El valor que muestra la expresión lambda onKeyEvent indica si se controla el evento de tecla o no. Muestra true si tu app controla el evento de tecla, lo que detiene la propagación del evento.

En el siguiente fragmento, se muestra cómo llamar a una función doSomething() cuando el usuario suelta la tecla S en el 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

Un objeto KeyEvent tiene los siguientes atributos que indican si se presionan las teclas modificadoras:

Sé específico cuando describas los eventos de tecla que controla tu app. En el siguiente fragmento, se llama a una doSomething() función solo si el usuario suelta solo la tecla S. Si el usuario presiona alguna tecla modificadora, como la tecla Shift, la app no llama a la función.

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 clic de la barra espaciadora y la tecla Intro

Las teclas Spacebar y Enter también activan eventos de clic. Por ejemplo, los usuarios pueden activar o desactivar (reproducir o pausar) la reproducción de contenido multimedia con la tecla Spacebar o Enter controlando los eventos de clic de la siguiente manera:

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

El modificador clickable intercepta eventos de tecla y llama a la devolución de llamada onClick() cuando se presiona la tecla Spacebar o Enter. Por eso, se llama a la función togglePausePlay() presionando la tecla Spacebar o Enter en el fragmento.

Eventos de tecla no consumidos

Los eventos de tecla no consumidos se propagan desde el componente en el que ocurrió el evento hasta el componente externo adjunto. En el siguiente ejemplo, el InnerComponent consume eventos de tecla cuando se suelta la tecla S, por lo que el OuterComponent no recibe ningún evento de tecla activado al soltar la tecla S. Por eso, nunca se llama a la función actionB().

El OuterComponent puede controlar otros eventos de tecla en InnerComponent, como soltar la tecla D, Se llama a la función actionC() porque el evento de tecla para soltar la tecla D se propaga al 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

En algunos casos de uso, deseas interceptar un evento de tecla antes de que active la acción predeterminada. Agregar combinaciones personalizadas a un TextField es un ejemplo típico. El siguiente fragmento permite a los usuarios pasar al siguiente componente enfocable presionando la 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
        }
    }
)

De forma predeterminada, el componente TextField agrega un carácter de tabulación cada vez que los usuarios presionan la tecla Tab, incluso si el evento de tecla se controla con el modificador onKeyEvent. Para mover el enfoque del teclado sin agregar ningún carácter de tabulación, controla el evento de tecla antes de activar las acciones asociadas con el evento de tecla, como en el fragmento. La expresión lambda onKeyPreviewEvent() intercepta el evento de tecla mostrando true.

El componente superior puede interceptar el evento de tecla que ocurre en sus elementos secundarios. En el siguiente fragmento, se llama a la función previewSKey() cuando los usuarios presionan la tecla S, en lugar de llamar a la función 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")
    }
}

La expresión lambda onPreviewKeyEvent() para el componente Box tampoco se activa cuando los usuarios presionan la tecla Tab. Primero, se llama a la expresión lambda onPreviewKeyEvent() en el componente superior y, luego, se llama a onPreviewKeyEvent() en el componente secundario. Puedes implementar combinaciones de teclas en toda la pantalla utilizando este comportamiento.

Recursos adicionales