[Перевод] Как закрыть/скрыть программную клавиатуру в Android

Оригинал: Close/Hide the Soft Keyboard in Android

От переводчика: без шуток, а можно было бы создать более простое решение для такой казалось бы простой и частой задачи.

Предисловие: Если вы читали какую-либо из моих статей по Android, то уже знаете, что я думаю о худшем SDK на Земле (английский). Эта статья только укрепила это мнение. К моему разочарованию, я создал то, что стало топ ответом Stack Overflow в вопросе по поводу сокрытия клавиатуры в Android. И хоть я и счастлив, что был полезен. Но так же я бы хотел, чтобы SDK не был таким отстойным и не требовало написания целой статьи для подобного.

Как закрыть или скрыть программную клавиатуру в Android?

Вопрос простой. БЕЗУМНО сложный ответ.

Чтобы помочь прояснить последующее безумия, я бы хотел извиниться от имени всех пользователей Android за отвратительное отношение Google к программной клавиатуре. Причина, по которой на простой вопрос StackOverflow есть так много разных ответов — ужасный дизайн этого API (как и многих других в Android). Не смог придумать как это сказать мягче.

Я хочу спрятать клавиатуру. Я ожидаю, что Android предоставит что-то вроде: Keyboard.hide()

Конец. Спасибо.

Но у Android есть проблема. Вы должны использовать для этого InputMethodManager. ОК, ладно, это Android API для клавиатуры.

НО! Вам так же нужно иметь Context для доступа к IMM. Вот теперь у нас проблема. Возможно я хочу скрыть клавиатуру из статического или вспомогательного класса, которые не используют и не нуждаются ни в каких Context. Но еще хуже то, что IMM требует от вас указать для какого View (или того хуже Window) вы хотите скрыть клавиатуру.

Это и делает из простой операции целое испытание.

Дорогой Google: Когда я ищу рецепт торта, то нет на Земле никакого RecipeProvider, который откажет в выдаче рецепта, пока я не укажу КТО будет есть этот торт и ГДЕ он будет это делать.

Скрываем клавиатуру из Activity

Я создал статическую функцию, которая надежно выполняет эту задачу (если вы вызываете её из Activity).

Прим. в оригинале используют Java, но у меня все проекты уже на Kotlin, да и язык сейчас все популярнее, а потому добавлю немного измененный аналогичный код.

Помните, что этот код работает только при вызове из Activity. Выше есть вызов currentFocus целевой активности для получения правильного windowToken.

Прячем клавиатуру из Fragment

Но предположим, что вы хотите спрятать клавиатуру из EditText диалогового окна? Функцию выше использовать не выйдет:

hideKeyboard(getActivity()); //не работает

И не работает она потому, что вы будете передавать ссылку на хост-активность, которая не имеет фокуса при активном фрагменте! Вау!

Итак, чтобы скрыть клавиатуру из фрагмента я прибегаю к более низкому уровню, и более уродливому:

Ниже приведена дополнительная информация, полученная из-за потраченного времени в погоне за решением.

О ‘windowSoftInputMode’

Еще одна вещь, о которой нужно знать. По умолчанию Android автоматически назначает фокус на первый EditText или первый фокусируемый элемент вашей Activity. Очевидно, что InputMethod (обычное программная клавиатура) будет реагировать на событие фокуса и отображаться. Аттрибут windowSoftInputMode в AndroidManifest.xml с указанным значением stateAlwaysHidden указывает клавиатуре игнорировать этот автоматический фокус.

Почти невероятно, но похоже, что нет ничего для предотвращения открытия клавиатуры когда вы касаетесь элемента управления (если конечно вы не указали focusable=»false» или focusableInTouchMode=»false» элементу управления). По-видимому, windowSoftInputMode применяется только для автоматического фокуса, но не для фокуса, который срабатывает от прикосновения.

Исходя из этого имя stateAlwaysHidden (состояние всегда скрыто) очень плохое название. Вместо этого его стоило бы назвать ignoreInitialFocus.

Больше способов получения window token

Если у вас нет View с фокусом (может произойти если вы изменили фрагмент), то есть другие способы получения window token.

Эти альтернативы для строки val view = activity.currentFocus ?: View(activity). Но они не относятся явно к вашей активности.

Внутри фрагмента:

view = view?.rootView?.windowToken

При этом можно передать фрагмент в виде параметра:

view = fragment.view?.rootView?.windowToken

И еще одна полезность: очистить фокус во избежание повторного отображения клавиатуры если вы открыли приложение из фона можно одной линией: view.clearFocus().

Удачи!

Оставьте комментарий

Прокрутить наверх