Совместимость ввода на больших экранах

На устройствах с большими экранами пользователи часто взаимодействуют с приложениями с помощью клавиатуры, мыши, сенсорной панели, стилуса или геймпада. Чтобы разрешить приложению принимать входные данные с внешних устройств, выполните следующие действия:

  • Проверьте основные функции клавиатуры, например Ctrl+Z для отмены, Ctrl+C для копирования и Ctrl+S для сохранения. Список сочетаний клавиш по умолчанию можно найти в разделе Обработка действий с клавиатурой.
  • Проверьте поддержку расширенных функций клавиатуры, например навигацию с помощью клавиши Tab и клавиш со стрелками, подтверждение ввода текста с помощью клавиши Ввод, а также воспроизведение и приостановку мультимедиа с помощью Пробела.
  • Проверьте основные действия с помощью мыши, в том числе нажатие правой кнопки для открытия контекстного меню, изменение значков при наведении указателя и прокрутку колесиком мыши или сенсорной панели на пользовательских компонентах.
  • Проверьте устройства ввода, предназначенные для определенных приложений, например стилус, игровые контроллеры и MIDI-контроллеры для музыкальных приложений.
  • Добавьте поддержку дополнительных способов ввода, чтобы ваше приложение выделялось на фоне других. Например, в приложениях для диджеев можно использовать сенсорную панель в качестве кроссфейдера, в играх – захват мыши, а для пользователей, которые предпочитают клавиатуру, – сочетания клавиш.

Клавиатура

То, как ваше приложение реагирует на ввод с клавиатуры, влияет на удобство использования на большом экране. Существует три типа ввода с клавиатуры: навигация, нажатия клавиш и сочетания клавиш.

Навигация с помощью клавиатуры редко реализуется в приложениях, ориентированных на сенсорное управление, но пользователи ожидают, что она будет доступна, когда они работают с приложением и держат руки на клавиатуре. Навигация с помощью клавиатуры может быть необходима на телефонах, планшетах, складных устройствах и компьютерах для пользователей с особыми потребностями.

Во многих приложениях навигация с помощью клавиш со стрелками и клавиши Tab обрабатывается автоматически в рамках платформы Android. Например, некоторые компоненты composable по умолчанию поддерживают фокус, такие как Button или компонент composable с модификатором clickable. Как правило, для навигации с помощью клавиатуры не требуется дополнительный код. Чтобы включить навигацию с помощью клавиатуры для пользовательских композиций, которые по умолчанию не могут быть выбраны, добавьте модификатор focusable:

var color by remember { mutableStateOf(Green) }
Box(
    Modifier
        .background(color)
        .onFocusChanged { color = if (it.isFocused) Blue else Green }
        .focusable()
) {
    Text("Focusable 1")
}

Подробнее о том, как сделать компонент доступным для фокуса…

Когда фокус включен, платформа Android создает навигационную карту для всех компонентов, на которые можно переключиться, на основе их положения. Обычно это работает так, как ожидается, и дальнейшая разработка не требуется.

Однако Compose не всегда правильно определяет следующий элемент для навигации с помощью клавиши Tab в сложных компонентах, например в списках и вкладках, если один из компонентов является горизонтальным прокручиваемым элементом, который виден не полностью.

Чтобы управлять поведением фокуса, добавьте модификатор focusGroup к родительскому компоненту Composable коллекции компонентов Composable. Фокус перемещается на группу, а затем по элементам группы, прежде чем перейти к следующему компоненту, на который можно установить фокус. Пример:

Row {
    Column(Modifier.focusGroup()) {
        Button({}) { Text("Row1 Col1") }
        Button({}) { Text("Row2 Col1") }
        Button({}) { Text("Row3 Col1") }
    }
    Column(Modifier.focusGroup()) {
        Button({}) { Text("Row1 Col2") }
        Button({}) { Text("Row2 Col2") }
        Button({}) { Text("Row3 Col2") }
    }
}

Подробнее о том, как обеспечить согласованную навигацию с помощью групп фокуса…

Проверьте, можно ли получить доступ ко всем элементам интерфейса приложения, используя только клавиатуру. Часто используемые элементы должны быть доступны без мыши или сенсорного ввода.

Помните, что поддержка клавиатуры может быть необходима пользователям с ограниченными возможностями.

Сочетания клавиш

Для ввода текста, который обрабатывается экранной виртуальной клавиатурой (IME), например для TextField , приложения должны работать на устройствах с большими экранами без дополнительной разработки. Для нажатий клавиш, которые не могут быть предусмотрены фреймворком, приложения должны обрабатывать поведение самостоятельно. Это особенно актуально для приложений с собственными представлениями.

Например, в чат-приложениях клавиша Ввод используется для отправки сообщений, в мультимедийных приложениях клавиша Пробел – для запуска и остановки воспроизведения, а в играх клавиши W, A, S и D – для управления движением.

Отдельные нажатия клавиш можно обрабатывать с помощью модификатора onKeyEvent, который принимает лямбда-выражение, вызываемое, когда измененный компонент получает событие нажатия клавиши. Свойство KeyEvent#type позволяет определить, является ли событие нажатием (KeyDown) или отпусканием (KeyUp) клавиши:

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

Вы также можете переопределить обратный вызов onKeyUp() и добавить ожидаемое поведение для каждого полученного кода клавиши:

kotlin override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { return when (keyCode) { KeyEvent.KEYCODE_ENTER -> { sendChatMessage() true } KeyEvent.KEYCODE_SPACE -> { playOrPauseMedia() true } else -> super.onKeyUp(keyCode, event) } }

Событие onKeyUp происходит, когда клавиша отпускается. Использование обратного вызова позволяет приложению не обрабатывать несколько событий onKeyDown, если клавиша удерживается или отпускается медленно. Игры и приложения, которым нужно определить момент нажатия клавиши или удерживает ли пользователь клавишу, могут отслеживать событие onKeyDown и обрабатывать повторяющиеся события onKeyDown самостоятельно.

Подробнее о том, как обрабатывать действия с клавиатурой…

Ярлыки

При использовании аппаратной клавиатуры должны работать стандартные сочетания клавиш, в которых используются клавиши Ctrl, Alt, Shift и Meta. Если в приложении не реализованы сочетания клавиш, пользователям может быть неудобно им пользоваться. Опытные пользователи также ценят сочетания клавиш для часто используемых задач в приложениях. Ярлыки делают приложение удобнее и выделяют его среди других.

Вот некоторые из них: Ctrl+S (сохранить), Ctrl+Z (отменить) и Ctrl+Shift+Z (повторить). Список сочетаний клавиш по умолчанию приведен в статье Как обрабатывать действия, выполняемые с помощью клавиатуры.

Объект KeyEvent имеет следующие атрибуты, указывающие, нажаты ли клавиши-модификаторы:

Пример:

Box(
    Modifier.onKeyEvent {
        if (it.isAltPressed && it.key == Key.A) {
            println("Alt + A is pressed")
            true
        } else {
            false
        }
    }
    .focusable()
)

Дополнительную информацию можно найти в следующих источниках:

Стилус

Многие устройства с большими экранами поставляются со стилусом. Приложения Android обрабатывают стилус как ввод с сенсорного экрана. К некоторым устройствам также можно подключить графический планшет через USB или Bluetooth, например Wacom Intuos. Приложения Android могут получать данные через Bluetooth, но не через USB.

Чтобы получить доступ к объектам стилуса MotionEvent, добавьте модификатор pointerInteropFilter к поверхности для рисования. Реализуйте класс ViewModel с методом, который обрабатывает события движения. Передайте этот метод в качестве лямбда-функции onTouchEvent модификатора pointerInteropFilter:

@Composable
@OptIn(ExperimentalComposeUiApi::class)
fun DrawArea(modifier: Modifier = Modifier) {
   Canvas(modifier = modifier
       .clipToBounds()
       .pointerInteropFilter {
           viewModel.processMotionEvent(it)
       }

   ) {
       // Drawing code here.
   }
}

Объект MotionEvent содержит информацию о событии:

Исторические данные

Android объединяет входные события в пакеты и доставляет их один раз за кадр. Стилус может передавать данные о событиях с гораздо большей частотой, чем дисплей. При создании приложений для рисования проверяйте события, которые могли произойти недавно, с помощью следующих API getHistorical:

Распознавание касаний ладони

Когда пользователи рисуют, пишут или взаимодействуют с вашим приложением с помощью стилуса, они иногда касаются экрана ладонью. Событие касания (ACTION_DOWN или ACTION_POINTER_DOWN) может быть передано в приложение до того, как система распознает и проигнорирует случайное касание ладонью.

Android отменяет события касания ладонью, отправляя MotionEvent. Если ваше приложение получит ACTION_CANCEL, отмените жест. Если ваше приложение получает ACTION_POINTER_UP, проверьте, задано ли значение FLAG_CANCELED. Если да, отмените жест.

Не проверяйте только FLAG_CANCELED. В Android 13 (уровень API 33) и более поздних версиях система устанавливает флаг FLAG_CANCELED для событий ACTION_CANCEL, но не делает этого в более ранних версиях Android.

Android 12

В Android 12 (уровень API 32) и более ранних версиях функция распознавания касания ладонью работает только для событий касания одним пальцем. Если единственным указателем является касание ладони, система отменяет событие, устанавливая значение ACTION_CANCEL в объекте события движения. Если другие указатели не работают, система устанавливает значение ACTION_POINTER_UP, которого недостаточно для определения того, что пользователь опирается на экран ладонью.

Android 13

В Android 13 (уровень API 33) и более поздних версиях, если прикосновение ладони является единственным указателем, система отменяет событие, задавая значения ACTION_CANCEL и FLAG_CANCELED в объекте события движения. Если другие указатели не работают, система задает значения ACTION_POINTER_UP и FLAG_CANCELED.

Когда приложение получает событие движения с ACTION_POINTER_UP, проверьте, есть ли в нем FLAG_CANCELED. Это позволит определить, указывает ли событие на отклонение ладони (или отмену другого события).

Приложения для заметок

В ChromeOS есть специальное намерение, которое показывает пользователям зарегистрированные приложения для создания заметок. Чтобы зарегистрировать приложение как приложение для заметок, добавьте в манифест приложения следующие строки:

<intent-filter>
    <action android:name="org.chromium.arc.intent.action.CREATE_NOTE" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

Когда приложение зарегистрировано в системе, пользователь может выбрать его в качестве приложения для заметок по умолчанию. При запросе новой заметки приложение должно создать пустую заметку, готовую к вводу с помощью стилуса. Когда пользователь захочет добавить аннотацию к изображению (например, к скриншоту или скачанному изображению), приложение запустится с намерением ClipData, содержащим один или несколько объектов с URI content://. Приложение должно создать заметку, используя первое прикрепленное изображение в качестве фонового, и перейти в режим, в котором пользователь может рисовать на экране стилусом.

Как тестировать намерения для заметок без стилуса

Чтобы проверить, правильно ли приложение реагирует на запросы для создания заметок без активного стилуса, используйте следующий метод для отображения вариантов создания заметок в ChromeOS:

  1. Как переключиться в режим разработчика и сделать устройство доступным для записи
  2. Нажмите Ctrl+Alt+F2, чтобы открыть терминал.
  3. Выполните команду sudo vi /etc/chrome_dev.conf.
  4. Нажмите i, чтобы изменить и добавить --ash-enable-palette в новую строку в конце файла.
  5. Чтобы сохранить изменения, нажмите Esc, а затем введите :, w, q и нажмите Ввод.
  6. Нажмите Ctrl + Alt + F1, чтобы вернуться к обычному интерфейсу ChromeOS.
  7. Выйдите из аккаунта и войдите снова

На панели должна появиться кнопка меню стилуса:

  • Нажмите кнопку стилуса на панели и выберите Новая заметка. Откроется пустая заметка к чертежу.
  • Сделайте скриншот. На панели выберите кнопку стилуса > Сделать снимок экрана или скачайте изображение. В уведомлении должна быть кнопка Добавить аннотацию к изображению. Приложение должно запуститься, а изображение – быть готовым к аннотированию.

Поддержка мыши и сенсорной панели

Большинству приложений нужно обрабатывать только три события, связанные с большими экранами: нажатие правой кнопки мыши, наведение указателя и перетаскивание.

Нажатие правой кнопки мыши

Любые действия, которые приводят к тому, что приложение показывает контекстное меню, например нажатие и удерживание элемента списка, должны также реагировать на события, связанные с нажатием правой кнопки мыши.

Чтобы обрабатывать события нажатия правой кнопки мыши, приложения должны зарегистрировать View.OnContextClickListener:

Box(modifier = Modifier.fillMaxSize()) {
    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { context ->
            val rootView = FrameLayout(context)
            val onContextClickListener =
                View.OnContextClickListener { view ->
                    showContextMenu()
                    true
                }
            rootView.setOnContextClickListener(onContextClickListener)
            rootView
        },
    )
}

Подробнее о том, как создать контекстное меню…

Наведите указатель

Обрабатывая события наведения указателя, вы можете сделать макеты приложений более удобными и привлекательными. Это особенно важно для пользовательских компонентов:

Вот два наиболее распространенных примера:

  • Указывать пользователям, является ли элемент интерактивным, например можно ли нажать на него или отредактировать его, изменяя значок указателя мыши.
  • Добавление визуальной обратной связи для элементов в большом списке или сетке, когда указатель наведен на них.

Перетаскивание

В многооконном режиме пользователи ожидают, что смогут перетаскивать объекты между приложениями. Это относится как к компьютерам, так и к планшетам, телефонам и складным устройствам в режиме разделенного экрана.

Подумайте, будут ли пользователи перетаскивать объекты в ваше приложение. Например, в фоторедактор можно перетаскивать фотографии, в аудиоплеер – аудиофайлы, а в графический редактор – фотографии.

Чтобы добавить поддержку перетаскивания, ознакомьтесь с разделом ПеретаскиваниеВключите перетаскивание

Особые условия для ChromeOS

Расширенная поддержка указателя

Приложения, которые выполняют расширенную обработку ввода с помощью мыши и сенсорной панели, должны реализовать модификатор pointerInput, чтобы получить PointerEvent:

@Composable
private fun LogPointerEvents(filter: PointerEventType? = null) {
    var log by remember { mutableStateOf("") }
    Column {
        Text(log)
        Box(
            Modifier
                .size(100.dp)
                .background(Color.Red)
                .pointerInput(filter) {
                    awaitPointerEventScope {
                        while (true) {
                            val event = awaitPointerEvent()
                            // handle pointer event
                            if (filter == null || event.type == filter) {
                                log = "${event.type}, ${event.changes.first().position}"
                            }
                        }
                    }
                }
        )
    }
}

Изучите объект PointerEvent, чтобы определить:

  • PointerType: мышь, стилус, сенсорный экран и т. д. от PointerEvent#changes.
  • PointerEventType – действия указателя, например нажатие, перемещение, прокрутка и отпускание.

Игровые контроллеры

Некоторые устройства Android с большим экраном поддерживают до четырех игровых контроллеров. Используйте стандартные API контроллеров игр Android (см. статью Поддержка контроллеров игр).

Кнопки игрового контроллера сопоставляются с распространенными значениями по стандартной схеме. Однако производители контроллеров не всегда используют одинаковые схемы. Вы можете значительно улучшить удобство для пользователей, если разрешите им выбирать разные популярные схемы управления. Подробнее об обработке нажатий кнопок геймпада…

Режим ввода перевода

В ChromeOS по умолчанию включен режим перевода ввода. В большинстве случаев этот режим позволяет приложениям Android работать на компьютере так же, как на мобильном устройстве. Например, автоматически включается прокрутка двумя пальцами на сенсорной панели, прокрутка колесиком мыши и сопоставление координат дисплея с координатами окна. Как правило, разработчикам приложений не нужно реализовывать эти функции самостоятельно.

Если в приложении реализовано специальное поведение при вводе, например определено специальное действие сжатия двумя пальцами на сенсорной панели, или если преобразование ввода не обеспечивает события ввода, ожидаемые приложением, вы можете отключить режим преобразования ввода, добавив в манифест Android следующий тег:

<uses-feature
    android:name="android.hardware.type.pc"
    android:required="false" />

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