Готовимся к Windows 11: добавляем поддержку полноценной клавиатуры в Android-приложение

—

от автора

Многие мобильные приложения уже могут конкурировать с полноценными десктопными версиями, а иногда и превосходить их. Офисные пакеты, фоторедакторы и IDE вполне неплохо работают на портативных девайсах. Samsung, например, даже сделал специальный режим DeX Mode, который позволяет подключить к смартфону монитор и периферию.

А скорый релиз Windows 11 с возможностью устанавливать любые APK-файлы прямо намекает, что пора озаботиться поддержкой десктопных режимов в своих мобильных приложениях. Один из шагов к этому — добавить полноценную поддержку клавиатуры, чем сегодня и займёмся.

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

Навигация по RecyclerView 

Google в RecyclerView (в отличие от ListView) не стал добавлять нативную поддержку клавиатур и D-падов для навигации, поэтому придётся добавлять её самим.

Реализация навигации во многом зависит от используемого layout-менеджера, наличия фокусируемых вьюх (focusable views) в списке, возможности мультивыбора и других деталей. Но базовый подход не поменяется. Для добавления поддержки клавиатуры в RecyclerView нужно только дополнить адаптер, не затрагивая остальную часть приложения. 

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

override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {    super.onAttachedToRecyclerView(recyclerView)    recyclerView.setOnKeyListener{_, keyCode, event-> if (event.action!= KeyEvent.ACTION_DOWN) {            return@setOnKeyListener false        }         when (keyCode) {            KeyEvent.KEYCODE_DPAD_DOWN-> selectNext(recyclerView)            KeyEvent.KEYCODE_DPAD_UP-> selectPrevious(recyclerView)            else -> return@setOnKeyListener false        }        return@setOnKeyListener true  } }

Сложность реализации методов selectNext() и selectPrevious() зависит целиком от ваших пожеланий. В общем случае всё сводится к:

private fun selectNext(recyclerView: RecyclerView) {    val oldSelectedPosition = selectedPosition    selectedPosition = if (selectedPosition.isLast()) {        0    } else {        selectedPosition + 1    }    notifyItemChanged(selectedPosition)    notifyItemChanged(oldSelectedPosition)    recyclerView.smoothScrollToPosition(selectedPosition) }

Горячие клавиши для toolbar menu

Пожалуй, наиболее простой способ добавить шорткаты в приложение — использовать атрибуты android:alphabeticShortcut и android:numericShortcut в ваших <menu>-файлах. 

Для примера меню вида:

<menu xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto">    <item        android:id="@+id/menuAddTask"        android:icon="@drawable/ic_add_task"        app:showAsAction="always"        android:title="@string/add_task"        android:alphabeticShortcut="n"/>     <item        android:id="@+id/menuFilter"        android:icon="@drawable/ic_filter"        app:showAsAction="always"        android:title="@string/show_filter"        android:alphabeticShortcut="f"/> </menu> 

Такое меню будет, помимо обычных нажатий, автоматически поддерживать сочетания Ctrl+F и Ctrl+N для выполнения необходимых действий, описанных в методе onOptionsItemSelected().

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

И для этого можно сделать кастомные хоткеи.

Настраиваем кастомные горячие клавиши

Для более тонкой настройки класс KeyEvent предлагает набор методов, чтобы определить, зажата ли дополнительная клавиша во время срабатывания метода onKey: 

isCtrlPressed() isShiftPressed() isFunctionPressed() isAltPressed()

Возьмём для примера to-do-лист, и назначим удаление выбранной задачи по нажатию Shift+Delete. Для этого достаточно добавить в слушатель нажатий из предыдущего примера такой код:

if (keyCode == KeyEvent.KEYCODE_DEL && event.isShiftPressed && event.repeatCount == 0) {    deleteSelectedTask() }

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

Рассказываем о горячих клавишах пользователям

Мы не можем рассчитывать, что пользователь пойдет смотреть FAQ на экране саппорта, поэтому в Android 7.0 добавили сочетание Meta+/ (или Win+/ на соответствующих клавиатурах), которое открывает поп-ап с глобальными горячими клавишами системы. В него мы и добавим наши горячие клавиши для удаления или создания новой задачи.

Для этого нужно переопределить метод onProvideKeyboardShortcuts() и добавить в него новую группу хоткеев:

@TargetApi(Build.VERSION_CODES.N) override fun onProvideKeyboardShortcuts(data: MutableList<KeyboardShortcutGroup>, menu: Menu?, deviceId: Int) {   super.onProvideKeyboardShortcuts(data, menu, deviceId)   val additionalShortcuts = mutableListOf<KeyboardShortcutInfo>()   with(additionalShortcuts) {      add(KeyboardShortcutInfo("Delete task", KeyEvent.KEYCODE_DEL, KeyEvent.META_SHIFT_ON))      add(KeyboardShortcutInfo("New task", KeyEvent.KEYCODE_N, KeyEvent.META_CTRL_ON))   }   data.add(KeyboardShortcutGroup("My app custom shortcuts", additionalShortcuts)) }

Результат выглядит так:

Для устройств с версией ОС меньше 7.0 придётся делать своё решение:

if (keyCode == KeyEvent.KEYCODE_SLASH && event.isMetaPressed && Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {    showCustomShortcutsDialogFragment()    return true }

Заключение 

Поддержка клавиатуры в Аndroid-приложениях не требует каких-то значительных усилий и реализуется буквально несколькими методами. А значит — это очень простой способ улучшить пользовательский опыт в проекте. И им точно не стоит пренебрегать.


ссылка на оригинал статьи https://habr.com/ru/articles/571952/