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

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

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

Клавиатура

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

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

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

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

Подробнее о том, как сделать composable-функцию фокусируемой…

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

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

Чтобы управлять поведением фокуса, добавьте модификатор 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 обрабатывают ввод стилусом как ввод на сенсорном экране. К некоторым устройствам можно подключить графический планшет, например Wacom Intuos, через USB или Bluetooth. Приложения 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
        },
    )
}

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

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

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

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

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

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

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

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

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

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