Обработка действий с клавиатурой

Когда пользователь переходит к редактируемому текстовому компоненту, например 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. Модификатор принимает лямбда-выражение, которое вызывается, когда измененный компонент получает событие нажатия клавиши. Ключевое событие описывается как объект KeyEvent. Информацию о каждом ключевом событии можно получить, обратившись к объекту в лямбда-функции, переданной модификатору onKeyEvent.

Нажатие клавиши отправляет два ключевых события. Одно срабатывает, когда пользователь нажимает клавишу, а другое – когда отпускает. Вы можете различать эти два ключевых события, используя атрибут type объекта KeyEvent.

Возвращаемое значение лямбда-функции onKeyEvent указывает, обработано ли ключевое событие. Если приложение обрабатывает событие нажатия клавиши, возвращается значение true, и распространение события прекращается.

В следующем фрагменте кода показано, как вызвать функцию doSomething(), когда пользователь отпускает клавишу S на компоненте Box:

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

События кликов по клавишам Пробел и Ввод

Клавиши Пробел и Ввод также вызывают события клика. Например, пользователи могут запускать и приостанавливать воспроизведение медиаконтента с помощью клавиш Пробел или Ввод, обрабатывая события клика следующим образом:

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

Модификатор clickable перехватывает события клавиш и вызывает обратный вызов onClick() при нажатии клавиши Пробел или Ввод. Поэтому функция togglePausePlay() вызывается нажатием клавиши Пробел или Ввод в фрагменте кода.

Неиспользованные ключевые события

Необработанные ключевые события передаются из компонента, в котором произошло событие, во внешний компонент. В примере ниже InnerComponent использует ключевые события, когда отпускается клавиша S, поэтому 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
        }
    }
)

По умолчанию компонент TextField добавляет символ табуляции каждый раз, когда пользователи нажимают клавишу Tab, даже если ключевое событие обрабатывается с помощью модификатора onKeyEvent. Чтобы переместить фокус клавиатуры, не добавляя символы табуляции, обработайте событие клавиши до запуска связанных с ним действий, как показано во фрагменте кода. Лямбда-выражение onKeyPreviewEvent() перехватывает событие нажатия клавиши, возвращая 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")
    }
}

Лямбда-выражение onPreviewKeyEvent() для компонента Box также не срабатывает, когда пользователи нажимают клавишу Tab. Сначала вызывается лямбда-функция onPreviewKeyEvent() в родительском компоненте, а затем onPreviewKeyEvent() в дочернем. Используя это поведение, вы можете реализовать быстрые клавиши для всего экрана.

Дополнительные ресурсы