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