Чтобы прояснить это безумие, я хотел бы начать с извинения от имени всех пользователей Android за совершенно нелепое обращение Google с программной клавиатурой. Причина в том, что существует так много разных ответов на один и тот же простой вопрос, потому что этот API, как и многие другие в Android, разработан ужасно. Я не могу придумать никакого вежливого способа заявить об этом.
Я хочу спрятать клавиатуру. Я ожидаю предоставить Android следующее утверждение: Keyboard.hide()
. Конец. Большое спасибо. Но у Android есть проблема. Вы должны использовать InputMethodManager
, чтобы скрыть клавиатуру. Хорошо, хорошо, это API-интерфейс Android к клавиатуре. НО! Вам необходимо иметь Context
, чтобы получить доступ к IMM. Теперь у нас есть проблема. Возможно, я захочу спрятать клавиатуру от статического или служебного класса, который не нужен или не нужен для любого Context
. или И еще хуже, IMM требует, чтобы вы указали, какую View
(или еще хуже, какую Window
) вы хотите скрыть клавиатуру ОТ.
Это то, что делает скрытие клавиатуры таким сложным. Уважаемый Google: Когда я просматриваю рецепт торта, на Земле нет ни одного RecipeProvider
, который отказался бы предоставить мне этот рецепт, если я сначала не отвечу, КТО будет съеден торт И где он будет его есть! !
Эта печальная история заканчивается ужасной правдой: чтобы скрыть клавиатуру Android, вам потребуется предоставить 2 формы идентификации: Context
и View
или Window
.
Я создал метод статической утилиты, который может выполнять работу ОЧЕНЬ беспроблемно, при условии, что вы вызываете его из Activity
.
public static void hideKeyboard(Activity activity) {
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
//Find the currently focused view, so we can grab the correct window token from it.
View view = activity.getCurrentFocus();
//If no view currently has focus, create a new one, just so we can grab a window token from it
if (view == null) {
view = new View(activity);
}
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
Помните, что этот служебный метод работает ТОЛЬКО при вызове из Activity
! Приведенный выше метод вызывает getCurrentFocus
целевого объекта Activity
, чтобы получить соответствующий маркер окна.
Но предположим, что вы хотите скрыть клавиатуру от EditText
, размещенного на DialogFragment
? Вы не можете использовать метод выше для этого:
hideKeyboard(getActivity()); //won't work
Это не будет работать, потому что вы будете передавать ссылку на хост Fragment
Activity
, который не будет иметь сфокусированного управления, пока отображается Fragment
! Вот Это Да! Итак, чтобы скрыть клавиатуру от фрагментов, я прибегаю к более низкому уровню, более распространенному и более уродливому:
public static void hideKeyboardFrom(Context context, View view) {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
Ниже приведена дополнительная информация, полученная из-за потраченного времени в погоне за этим решением:
Об окне SoftInputMode
Есть еще один момент раздора, о котором нужно знать. По умолчанию Android автоматически назначит начальный фокус первому EditText
или фокусируемому элементу управления в Activity
. Естественно, что InputMethod (обычно это программная клавиатура) будет реагировать на событие фокуса, показывая себя. Атрибут windowSoftInputMode
в AndroidManifest.xml
, установленный на stateAlwaysHidden
, заставляет клавиатуру игнорировать этот автоматически назначенный начальный фокус.
<activity
android:name=".MyActivity"
android:windowSoftInputMode="stateAlwaysHidden"/>
Почти невероятно, что, похоже, ничего не делает для предотвращения открытия клавиатуры при прикосновении к элементу управления (если focusable="false"
и / или focusableInTouchMode="false"
не назначены элементу управления). По-видимому, настройка windowSoftInputMode применяется только к событиям автоматической фокусировки, а не к событиям фокусировки, вызванным событиями касания.
Следовательно, stateAlwaysHidden
действительно ОЧЕНЬ плохо назван. Возможно, вместо этого следует назвать ignoreInitialFocus
.
Надеюсь, это поможет.
ОБНОВЛЕНИЕ: больше способов получить маркер окна
Если нет сфокусированного представления (например, это может произойти, если вы только что изменили фрагменты), есть другие представления, которые предоставят полезный маркер окна.
Это альтернативы приведенному выше коду if (view == null) view = new View(activity);
Они не относятся явно к вашей деятельности.
Внутри класса фрагмента:
view = getView().getRootView().getWindowToken();
С учетом фрагмента fragment
в качестве параметра:
view = fragment.getView().getRootView().getWindowToken();
Начиная с вашего тела контента:
view = findViewById(android.R.id.content).getRootView().getWindowToken();
ОБНОВЛЕНИЕ 2: Снимите фокус, чтобы не показывать клавиатуру снова, если вы откроете приложение из фона
Добавьте эту строку в конец метода:
view.clearFocus();