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

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

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

Клавиатура

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

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

Для многих приложений навигация по клавишам со стрелками и клавишам 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() и добавить ожидаемое поведение для каждого полученного кода ключа:

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.

Андроид 12

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

Андроид 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:// . Приложение должно создать заметку, которая использует первое прикрепленное изображение в качестве фонового изображения, и войти в режим, в котором пользователь может рисовать на экране стилусом.

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

[TBD удалить раздел.]

Чтобы проверить, правильно ли приложение реагирует на намерения создания заметок без активного стилуса, используйте следующий метод для отображения параметров создания заметок в 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" />

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