Para ajudar a esclarecer essa loucura, gostaria de começar pedindo desculpas, em nome de todos os usuários do Android, pelo tratamento ridículo do Google com o teclado virtual. A razão pela qual existem tantas respostas, cada uma diferente, para a mesma pergunta simples é porque essa API, como muitas outras no Android, é horrivelmente projetada. Não consigo pensar em nenhuma maneira educada de dizer isso.
Eu quero esconder o teclado. Espero para fornecer Android com a seguinte declaração: Keyboard.hide()
. O fim. Muito obrigado. Mas o Android tem um problema. Você deve usar o InputMethodManager
para ocultar o teclado. OK, tudo bem, esta é a API do Android para o teclado. MAS! Você precisa ter um Context
para obter acesso ao IMM. Agora nós temos um problema. Talvez eu queira ocultar o teclado de uma classe estática ou de utilitário que não tenha utilidade ou necessidade Context
. ou E Pior ainda, o IMM exige que você especifique o que View
(ou pior, o que Window
) deseja ocultar do teclado.
É isso que torna a ocultação do teclado um desafio. Caro Google: Quando estou pesquisando a receita de um bolo, não há RecipeProvider
na Terra que se recusaria a me fornecer a receita, a menos que eu responda primeiro a QUEM o bolo será comido E onde será comido !!
Essa triste história termina com a feia verdade: para ocultar o teclado do Android, você precisará fornecer duas formas de identificação: Context
ae a View
ou a ou a Window
.
Eu criei um método de utilitário estático que pode fazer o trabalho MUITO solidamente, desde que você o chame de 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);
}
Esteja ciente de que este método utilitário só funciona quando chamado de um Activity
! O método acima chama getCurrentFocus
o destino Activity
para buscar o token de janela apropriado.
Mas suponha que você queira ocultar o teclado de um EditText
host em um DialogFragment
? Você não pode usar o método acima para isso:
hideKeyboard(getActivity()); //won't work
Isso não funcionará porque você passará uma referência ao Fragment
host do Activity
que não terá controle focado enquanto o Fragment
é mostrado! Uau! Portanto, para ocultar fragmentos do teclado, recorro ao nível inferior, mais comum e mais feio:
public static void hideKeyboardFrom(Context context, View view) {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
Abaixo estão algumas informações adicionais obtidas com mais tempo desperdiçado na busca desta solução:
Sobre windowSoftInputMode
Há ainda outro ponto de discórdia a ter em conta. Por padrão, o Android atribui automaticamente o foco inicial ao primeiro EditText
ou controle focalizável no seu Activity
. Segue-se naturalmente que o InputMethod (normalmente o teclado virtual) responderá ao evento de foco, mostrando-se. O windowSoftInputMode
atributo em AndroidManifest.xml
, quando definido como stateAlwaysHidden
, instrui o teclado a ignorar esse foco inicial atribuído automaticamente.
<activity
android:name=".MyActivity"
android:windowSoftInputMode="stateAlwaysHidden"/>
Quase inacreditavelmente, parece não fazer nada para impedir a abertura do teclado quando você toca no controle (a menos que focusable="false"
e / ou focusableInTouchMode="false"
esteja atribuído ao controle). Aparentemente, a configuração windowSoftInputMode se aplica apenas a eventos de foco automático, não a eventos de foco acionados por eventos de toque.
Portanto, stateAlwaysHidden
é MUITO mal nomeado de fato. Talvez devesse ser chamado em seu ignoreInitialFocus
lugar.
Espero que isto ajude.
UPDATE: Mais maneiras de obter um token de janela
Se não houver uma visão focada (por exemplo, pode acontecer se você acabou de alterar fragmentos), existem outras visualizações que fornecerão um token de janela útil.
Essas são alternativas para o código acima. if (view == null) view = new View(activity);
Elas não se referem explicitamente à sua atividade.
Dentro de uma classe de fragmento:
view = getView().getRootView().getWindowToken();
Dado um fragmento fragment
como parâmetro:
view = fragment.getView().getRootView().getWindowToken();
A partir do seu corpo de conteúdo:
view = findViewById(android.R.id.content).getRootView().getWindowToken();
ATUALIZAÇÃO 2: foco claro para evitar a exibição do teclado novamente se você abrir o aplicativo a partir do segundo plano
Adicione esta linha ao final do método:
view.clearFocus();