Pull to refresh
0
FUNCORP
Разработка развлекательных сервисов

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

Reading time4 min
Views9.2K

Многие мобильные приложения уже могут конкурировать с полноценными десктопными версиями, а иногда и превосходить их. Офисные пакеты, фоторедакторы и 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-приложениях не требует каких-то значительных усилий и реализуется буквально несколькими методами. А значит — это очень простой способ улучшить пользовательский опыт в проекте. И им точно не стоит пренебрегать.

Tags:
Hubs:
Total votes 44: ↑43 and ↓1+52
Comments16

Articles

Information

Website
funcorp.dev
Registered
Founded
Employees
101–200 employees
Location
Кипр
Representative
ulanana