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

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

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

Клавиатура

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

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

Для многих приложений навигация с помощью клавиш со стрелками и клавиши Tab обрабатывается автоматически фреймворком Android. Например, некоторые составные элементы по умолчанию являются фокусируемыми, такие как Button или элемент с модификатором 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 не всегда определяет правильный следующий элемент для навигации по вкладкам в сложных комбинируемых элементах, таких как вкладки и списки, например, когда один из комбинируемых элементов представляет собой горизонтальную прокручиваемую область, которая не полностью видна.

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

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Приложения должны вести себя должным образом на устройствах с большими экранами без дополнительной разработки. Для нажатий клавиш, которые фреймворк не может предусмотреть, приложения должны обрабатывать поведение самостоятельно. Это особенно важно для приложений с пользовательскими представлениями.

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

Подробную информацию о создании контекстных меню см. в разделе «Создание контекстного меню» .

Ховер

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

Два наиболее распространенных примера этого:

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

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

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

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

Для добавления поддержки перетаскивания см.ПеретаскиваниеА также ознакомьтесь с записью в блоге «Android на ChromeOS — реализация функции перетаскивания» .

Особые соображения для 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" />

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