Como ocultar o teclado virtual no android depois de clicar fora do EditText?


354

Ok, todo mundo sabe que, para ocultar um teclado, você precisa implementar:

InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);

Mas o grande problema aqui é como ocultar o teclado quando o usuário toca ou seleciona qualquer outro lugar que não EditTextseja o ou o softKeyboard?

Tentei usar o onTouchEvent()no meu pai, Activitymas isso só funciona se o usuário tocar fora de qualquer outra visualização e não houver rolagem.

Tentei implementar um toque, clique, focalize o ouvinte sem sucesso.

Até tentei implementar minha própria visualização de rolagem para interceptar eventos de toque, mas só consigo obter as coordenadas do evento e não clicar na visualização.

Existe uma maneira padrão de fazer isso? no iPhone foi muito fácil.


Bem, percebi que o scrollview não era realmente o problema, mas os rótulos existentes. A vista é um layout vertical com algo como: TextView, EditText, TextView, EditText, etc .. e os TextViews não vai deixar o EditText de foco solto e ocultar o teclado
htafoya

Você pode encontrar uma solução para getFields()aqui: stackoverflow.com/questions/7790487/…
Reto

Teclado pode ser fechado pressionando o botão de retorno, então eu diria que é questionável se isso vale a pena o esforço
gerrytan

4
Encontrei esta resposta: stackoverflow.com/a/28939113/2610855 A melhor.
Loenix

Respostas:


575

O seguinte trecho simplesmente oculta o teclado:

public static void hideSoftKeyboard(Activity activity) {
    InputMethodManager inputMethodManager = 
        (InputMethodManager) activity.getSystemService(
            Activity.INPUT_METHOD_SERVICE);
    inputMethodManager.hideSoftInputFromWindow(
        activity.getCurrentFocus().getWindowToken(), 0);
}

Você pode colocar isso em uma classe de utilitário ou, se estiver definindo em uma atividade, evite o parâmetro de atividade ou chame hideSoftKeyboard(this).

A parte mais complicada é quando chamá-lo. Você pode escrever um método que itere em todas as Viewsuas atividades e verificar se é um instanceof EditTextse não estiver registrando setOnTouchListenera nesse componente e tudo se encaixará. Caso você esteja se perguntando como fazer isso, é de fato bastante simples. Aqui está o que você faz, você escreve um método recursivo como o seguinte, na verdade, você pode usá-lo para fazer qualquer coisa, como configurar tipos de letra personalizados etc ... Aqui está o método

public void setupUI(View view) {

    // Set up touch listener for non-text box views to hide keyboard.
    if (!(view instanceof EditText)) {
        view.setOnTouchListener(new OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                hideSoftKeyboard(MyActivity.this);
                return false;
            }
        });
    }

    //If a layout container, iterate over children and seed recursion.
    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            View innerView = ((ViewGroup) view).getChildAt(i);
            setupUI(innerView);
        }
    }
}

Isso é tudo, basta chamar esse método depois de você setContentViewem sua atividade. Caso você esteja se perguntando qual parâmetro você passaria, é o iddo contêiner pai. Atribua um idao seu container pai como

<RelativeLayoutPanel android:id="@+id/parent"> ... </RelativeLayout>

e ligar setupUI(findViewById(R.id.parent)), isso é tudo.

Se você deseja usar isso efetivamente, poderá criar um Activitymétodo estendido e colocar esse método, além de fazer com que todas as outras atividades em seu aplicativo estendam essa atividade e chamem- setupUI()no no onCreate()método

Espero que ajude.

Se você usar mais de uma atividade, defina o ID comum para o layout pai, como <RelativeLayout android:id="@+id/main_parent"> ... </RelativeLayout>

Em seguida, estenda uma classe Activitye defina setupUI(findViewById(R.id.main_parent))Dentro dela OnResume()e estenda essa classe em vez de `` Atividadein your program


Aqui está uma versão Kotlin da função acima:

@file:JvmName("KeyboardUtils")

fun Activity.hideSoftKeyboard() {
    currentFocus?.let {
        val inputMethodManager = ContextCompat.getSystemService(this, InputMethodManager::class.java)!!
        inputMethodManager.hideSoftInputFromWindow(it.windowToken, 0)
    }
}

Eu não testei a mim mesmo, mas parece que funcionaria e, como tem críticas elevadas, alterarei a resposta aceita.
htafoya

4
Não deve ser muito difícil? Estou fora da programação do Android agora, então me corrija se estiver errado. De alguma forma, você pode rastrear o EditText focado a qualquer momento e solicitar que ele perca o foco durante um OnTouchEvent?
precisa saber é o seguinte

25
Não tenho certeza se alguém já passou por esse problema, mas isso causa uma falha no aplicativo quando você chama hideSoftKeyboard se nada estiver focado. Você pode resolver isso em torno da segunda linha do método comif(activity.getCurrentFocus() != null) {...}
Frank Cangialosi

14
O problema dessa abordagem é que ela assume que todas as outras visualizações nunca precisarão definir uma OnTouchListenerpara elas. Você pode simplesmente definir essa lógica em uma ViewGroup.onInterceptTouchEvent(MotionEvent)para uma visualização raiz.
Alex.F

2
Não funciona quando eu clicar em outros controles mantendo o teclado aberto
mohitum

285

Você pode conseguir isso executando as seguintes etapas:

  1. Torne a visualização principal (visualização de conteúdo da sua atividade) clicável e focável, adicionando os seguintes atributos

        android:clickable="true" 
        android:focusableInTouchMode="true" 
  2. Implementar um método hideKeyboard ()

        public void hideKeyboard(View view) {
            InputMethodManager inputMethodManager =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
  3. Por fim, defina o onFocusChangeListener do seu edittext.

        edittext.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (!hasFocus) {
                    hideKeyboard(v);
                }
            }
        });

Conforme indicado em um dos comentários abaixo, isso pode não funcionar se a exibição pai for um ScrollView. Nesse caso, o Clickable e focusableInTouchMode podem ser adicionados à exibição diretamente sob o ScrollView.


67
Na minha opinião, esta é a resposta correta. Código menos, há iterações desnecessárias ...
gattshjoty

14
Eu gosto muito dessa resposta. Uma coisa a observar é que isso não funcionou para mim ao adicionar clickablee focusableInTouchModeao meu ScrollViewelemento raiz . Eu tive que adicionar ao pai direto do meu EditTextque era um LinearLayout.
Adam Johns

10
Funcionou perfeitamente para mim. No entanto, se você tiver dois widgets do edittext, precisará se certificar de manipular o foco dos dois corretamente, caso contrário, alternará ocultando desnecessariamente o teclado.
Marka A

11
@MarkaA Não tive problemas com isso. Quando clico no outro EditText, o teclado permanece, se clicado no plano de fundo, ele se esconde como deveria. Eu tive algumas outras manipulações no onFocusChange, apenas alterando o plano de fundo do EditText quando ele tem foco, nada fency.
CularBytes 2/07

3
@ Srikant - Eu estava vendo cintilação também com vários textos de edição. Acabei de definir o onFocusChangeListener no pai, em vez de em cada texto de edição, e mudei a condição para dizer se (hasFocus) {hideKeyboard (v); } Não percebemos mais cintilação ao alternar entre os textos de edição.
manisha

69

Basta substituir o código abaixo na Atividade

 @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (getCurrentFocus() != null) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
    }
    return super.dispatchTouchEvent(ev);
}

4
Solução simples, Adicionar em atividade e vai cuidar do fragmento também
Rohit Maurya

11
Outra solução é criar BaseActivity e estendê-lo em todas as atividades
sumit sonawane

11
Solução brilhante
Ahmed Adel Ismail

11
você me salvou de piscar minha tela ao fechar o teclado. Afirmativo!
Dimas Mendes

11
Bom, mas era bom demais para ser verdade: simples, muito curto e funcionou ... infelizmente, há um problema: quando o teclado é exibido, toda vez que tocamos no EditText que solicitou o teclado, ele desce e automaticamente.
Chrysotribax

60

Acho a resposta aceita um pouco complicada.

Aqui está a minha solução. Adicione um OnTouchListenerao seu layout principal, ou seja:

findViewById(R.id.mainLayout).setOnTouchListener(this)

e coloque o seguinte código no método onTouch.

InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);

Dessa forma, você não precisa repetir todas as visualizações.


@roepit - estou recebendo uma classCastexception por tentar converter um layout em uma exibição. estou esquecendo de algo?
katzenhut

Você pode fazer referência ao seu código em algum lugar? Não sei dizer o que há de errado quando não consigo olhar para o seu layout e o código de atividade / fragmento etc.
roepit

Melhor resposta lá fora, ainda tentando entender como isso funciona.
User40797

isso funcionará se você clicar na barra de título do aplicativo?
user1506104

11
Isso funciona perfeitamente para esconder o teclado! Observe que isso na verdade não desfoca o EditText, apenas oculta o teclado. Para também desfocar o EditText, adicione, por exemplo, android:onClick="stealFocusFromEditTexts"ao xml da visualização principal e depois public void stealFocusFromEditTexts(View view) {}à sua atividade. O método on-click não precisa fazer nada, apenas deve existir para que a visualização pai seja focalizável / selecionável, o que é necessário para roubar o foco da criança. EditText
Jacob R

40

Eu tenho mais uma solução para ocultar o teclado:

InputMethodManager imm = (InputMethodManager) getSystemService(
    Activity.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);

Aqui passe HIDE_IMPLICIT_ONLYna posição de showFlage 0na posição de hiddenFlag. Fechará com força o teclado virtual.


3
Graças é trabalho ..... acima de tudo, eu tinha tentado, mas ele não está funcionando enquanto im recebendo o valor de texto dialogbox EditExt nd closoing dialogbox ...
PankajAndroid

Obrigado, isso está funcionando apenas, e é muito mais limpo que o outro acima! +1
Edmond Tamas

Está funcionando como o esperado, obrigado pela sua solução +1
tryp

Funciona como um encanto para mim. +1 para solução elegante.
saintjab

2
Desculpe, mas este método é de alternância, por isso, se estado do teclado já está fechado, ele irá mostrar o teclado
HendraWD

16

Bem, eu consigo resolver um pouco o problema, substitui o dispatchTouchEvent na minha atividade, lá estou usando o seguinte para ocultar o teclado.

 /**
 * Called to process touch screen events. 
 */
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {

    switch (ev.getAction()){
        case MotionEvent.ACTION_DOWN:
            touchDownTime = SystemClock.elapsedRealtime();
            break;

        case MotionEvent.ACTION_UP:
            //to avoid drag events
            if (SystemClock.elapsedRealtime() - touchDownTime <= 150){  

                EditText[] textFields = this.getFields();
                if(textFields != null && textFields.length > 0){

                    boolean clickIsOutsideEditTexts = true;

                    for(EditText field : textFields){
                        if(isPointInsideView(ev.getRawX(), ev.getRawY(), field)){
                            clickIsOutsideEditTexts = false;
                            break;
                        }
                    }

                    if(clickIsOutsideEditTexts){
                        this.hideSoftKeyboard();
                    }               
                } else {
                    this.hideSoftKeyboard();
                }
            }
            break;
    }

    return super.dispatchTouchEvent(ev);
}

EDIT: O método getFields () é apenas um método que retorna uma matriz com os campos de texto na exibição. Para evitar criar essa matriz a cada toque, criei uma matriz estática chamada sFields, que é retornada no método getFields (). Essa matriz é inicializada nos métodos onStart (), como:

sFields = new EditText[] {mUserField, mPasswordField};


Não é perfeito. O horário do evento de arrastar é baseado apenas em heurísticas; portanto, às vezes, ele não se oculta ao executar cliques longos, e eu também terminei criando um método para obter todos os editTexts por exibição; caso contrário, o teclado ocultaria e mostraria ao clicar em outro EditText.

Ainda assim, soluções mais limpas e mais curtas são bem-vindas


8
Para ajudar outras pessoas no futuro, você consideraria editar o código em sua resposta para incluir seu getFields()método? Não precisa ser exato, apenas um exemplo com talvez apenas alguns comentários indicando que ele retorna uma matriz de EditTextobjetos.
21411 Squonk

14

Use OnFocusChangeListener .

Por exemplo:

editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if (!hasFocus) {
            hideKeyboard();
        }
    }
});

Atualização : você também pode substituir onTouchEvent()sua atividade e verificar as coordenadas do toque. Se as coordenadas estiverem fora do EditText, oculte o teclado.


9
O problema é que o edittext não perde o foco quando clico em um Rótulo ou em outras visualizações que não são focalizáveis.
Htafoya

Neste caso, tenho mais uma solução. Eu atualizei a resposta.
Sergey Glotov 12/11/2010

2
onTouchEvent chamado a muitas vezes de modo que este não é uma boa prática, quer
Jesus Dimrix

13

Eu implementei o dispatchTouchEvent na Activity para fazer isso:

private EditText mEditText;
private Rect mRect = new Rect();
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final int action = MotionEventCompat.getActionMasked(ev);

    int[] location = new int[2];
    mEditText.getLocationOnScreen(location);
    mRect.left = location[0];
    mRect.top = location[1];
    mRect.right = location[0] + mEditText.getWidth();
    mRect.bottom = location[1] + mEditText.getHeight();

    int x = (int) ev.getX();
    int y = (int) ev.getY();

    if (action == MotionEvent.ACTION_DOWN && !mRect.contains(x, y)) {
        InputMethodManager input = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        input.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
    }
    return super.dispatchTouchEvent(ev);
}

e eu testei, funciona perfeito!


obras, mas problema nisso é que se tivermos mais de um EditText então precisamos considerar isso também, mas eu gostei sua resposta :-)
Lalit Poptani

getActionMasked (ev) foi descontinuado, agora use: final int action = ev.getActionMasked (); para a primeira linha.
Andrew

12

Uma maneira mais Kotlin e Design de materiais usando TextInputEditText (essa abordagem também é compatível com EditTextView ) ...

1.Faça clicável e focável a visualização pai (visualização de conteúdo de sua atividade / fragmento) adicionando os seguintes atributos

android:focusable="true"
android:focusableInTouchMode="true"
android:clickable="true"

2.Crie uma extensão para todo o View (dentro de um arquivo ViewExtension.kt, por exemplo):

fun View.hideKeyboard(){
    val inputMethodManager = context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
    inputMethodManager.hideSoftInputFromWindow(this.windowToken, 0)
}

3.Crie um BaseTextInputEditText que herda de TextInputEditText. Implemente o método onFocusChanged para ocultar o teclado quando a exibição não estiver focada:

class BaseTextInputEditText(context: Context?, attrs: AttributeSet?) : TextInputEditText(context, attrs){
    override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect)
        if (!focused) this.hideKeyboard()
    }
}

4. Basta chamar sua nova exibição personalizada em seu XML:

<android.support.design.widget.TextInputLayout
        android:id="@+id/textInputLayout"
        ...>

        <com.your_package.BaseTextInputEditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            ... />

    </android.support.design.widget.TextInputLayout> 

Isso é tudo. Não há necessidade de modificar seus controladores (fragmento ou atividade) para lidar com esse caso repetitivo.


Sim, mas eu gostaria que houvesse uma maneira mais simples!
devDeejay

11

Substituir público booleano dispatchTouchEvent (evento MotionEvent) em qualquer Atividade (ou estender a classe de Atividade)

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    View view = getCurrentFocus();
    boolean ret = super.dispatchTouchEvent(event);

    if (view instanceof EditText) {
        View w = getCurrentFocus();
        int scrcoords[] = new int[2];
        w.getLocationOnScreen(scrcoords);
        float x = event.getRawX() + w.getLeft() - scrcoords[0];
        float y = event.getRawY() + w.getTop() - scrcoords[1];

        if (event.getAction() == MotionEvent.ACTION_UP 
 && (x < w.getLeft() || x >= w.getRight() 
 || y < w.getTop() || y > w.getBottom()) ) { 
            InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(getWindow().getCurrentFocus().getWindowToken(), 0);
        }
    }
 return ret;
}

E é tudo o que você precisa fazer


essa foi a maneira mais fácil que encontrei para fazê-lo funcionar. trabalha com vários EditTexts e uma ScrollView
RJH

Testei com vários EditTexts; funciona! A única desvantagem é que, quando você faz um movimento de arrastar, ele também oculta.
RominaV

9

Modifiquei a solução de Andre Luis IM, consegui esta:

Criei um método utilitário para ocultar o teclado virtual da mesma maneira que Andre Luiz IM:

public static void hideSoftKeyboard(Activity activity) {
    InputMethodManager inputMethodManager = (InputMethodManager)  activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
    inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
}

Mas, em vez de registrar um OnTouchListener para todas as visualizações, que apresentam um desempenho ruim, registrei o OnTouchListener apenas para a visualização raiz. Como o evento borbulha até ser consumido (o EditText é uma das visualizações que o consome por padrão), se chega à visualização raiz, é porque não foi consumido, por isso fecho o teclado virtual.

findViewById(android.R.id.content).setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Utils.hideSoftKeyboard(activity);
        return false;
    }
});

11
Isso parece ser mais seguro para mim.
Superarts.org

9

Estou ciente de que esse tópico é bastante antigo, a resposta correta parece válida e há muitas soluções de trabalho por aí, mas acho que a abordagem descrita abaixo pode ter um benefício adicional em relação à eficiência e elegância.

Como preciso desse comportamento para todas as minhas atividades, criei uma classe CustomActivity herdando da classe Activity e "liguei" a função dispatchTouchEvent . Existem principalmente duas condições para cuidar:

  1. Se o foco permanecer inalterado e alguém estiver tocando fora do campo de entrada atual, ignore o IME
  2. Se o foco mudou e o próximo elemento focado não for uma instância de qualquer tipo de campo de entrada, então descarte o IME

Este é o meu resultado:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if(ev.getAction() == MotionEvent.ACTION_UP) {
        final View view = getCurrentFocus();

        if(view != null) {
            final boolean consumed = super.dispatchTouchEvent(ev);

            final View viewTmp = getCurrentFocus();
            final View viewNew = viewTmp != null ? viewTmp : view;

            if(viewNew.equals(view)) {
                final Rect rect = new Rect();
                final int[] coordinates = new int[2];

                view.getLocationOnScreen(coordinates);

                rect.set(coordinates[0], coordinates[1], coordinates[0] + view.getWidth(), coordinates[1] + view.getHeight());

                final int x = (int) ev.getX();
                final int y = (int) ev.getY();

                if(rect.contains(x, y)) {
                    return consumed;
                }
            }
            else if(viewNew instanceof EditText || viewNew instanceof CustomEditText) {
                return consumed;
            }

            final InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);

            inputMethodManager.hideSoftInputFromWindow(viewNew.getWindowToken(), 0);

            viewNew.clearFocus();

            return consumed;
        }
    }       

    return super.dispatchTouchEvent(ev);
}

Nota lateral: Além disso, atribuo esses atributos à visualização raiz, possibilitando limpar o foco em todos os campos de entrada e impedindo que os campos ganhem foco na inicialização da atividade (tornando o conteúdo visualizado como "coletor de foco"):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    final View view = findViewById(R.id.content);

    view.setFocusable(true);
    view.setFocusableInTouchMode(true);
}

Super está funcionando bem obrigado !! Eu coloquei um voto positivo para a sua resposta.
Vijay

2
Eu acho que é a melhor solução para layouts complexos. Mas eu encontrei duas desvantagens até agora: 1. O menu de contexto EditText não é clicável - qualquer clique nele causa perda de foco do EditText 2. Quando nosso EditText está na parte inferior de uma visualização e clicamos por muito tempo (para selecionar a palavra), depois o teclado mostrado como nosso "ponto de clique" está no teclado, não no EditText - então, perdemos o foco novamente: /
sosite

@sosite, acho que resolvi essas limitações na minha resposta; dê uma olhada.
Andy Dennie 24/09

6

Gostei da abordagem de chamada dispatchTouchEventfeita pelo htafoya, mas:

  • Não entendi a parte do temporizador (não sei por que medir o tempo de inatividade deve ser necessário?)
  • Não gosto de registrar / cancelar o registro de todos os EditTexts a cada alteração de exibição (pode haver muitas alterações de exibição e edições em hierarquias complexas)

Então, eu fiz essa solução um pouco mais fácil:

@Override
public boolean dispatchTouchEvent(final MotionEvent ev) {
    // all touch events close the keyboard before they are processed except EditText instances.
    // if focus is an EditText we need to check, if the touchevent was inside the focus editTexts
    final View currentFocus = getCurrentFocus();
    if (!(currentFocus instanceof EditText) || !isTouchInsideView(ev, currentFocus)) {
        ((InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE))
            .hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
    }
    return super.dispatchTouchEvent(ev);
}

/**
 * determine if the given motionevent is inside the given view.
 * 
 * @param ev
 *            the given view
 * @param currentFocus
 *            the motion event.
 * @return if the given motionevent is inside the given view
 */
private boolean isTouchInsideView(final MotionEvent ev, final View currentFocus) {
    final int[] loc = new int[2];
    currentFocus.getLocationOnScreen(loc);
    return ev.getRawX() > loc[0] && ev.getRawY() > loc[1] && ev.getRawX() < (loc[0] + currentFocus.getWidth())
        && ev.getRawY() < (loc[1] + currentFocus.getHeight());
}

Há uma desvantagem:

Mudar de um EditTextpara outro EditTextfaz com que o teclado oculte e reapresente - no meu caso, é desejado dessa maneira, porque mostra que você alternou entre dois componentes de entrada.


Esse método funcionou melhor em termos de capacidade de plug-and-play com meu FragmentActivity.
BillyRayCyrus

Obrigado, é o melhor caminho! Também adicionei a verificação da ação do evento: int action = ev.getActionMasked (); if (action == MotionEvent.ACTION_DOWN || ação == MotionEvent.ACTION_POINTER_DOWN) {...}
sergey.n

Obrigado ! Você economizou meu tempo ... Melhor resposta.
I.d007

6

Fundamento: Reconheço que não tenho influência, mas leve a minha resposta a sério.

Problema: ignore o teclado virtual ao clicar fora do teclado ou edite o texto com o mínimo de código.

Solução: Biblioteca externa conhecida como Butterknife.

Solução de uma linha:

@OnClick(R.id.activity_signup_layout) public void closeKeyboard() { ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); }

Solução mais legível:

@OnClick(R.id.activity_signup_layout) 
public void closeKeyboard() {
        InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}

Explicação: Vincule o OnClick Listener ao ID pai do Layout XML da atividade, para que qualquer clique no layout (não no texto ou teclado de edição) execute esse snippet de código que ocultará o teclado.

Exemplo: se o seu arquivo de layout for R.layout.my_layout e seu ID de layout for R.id.my_layout_id, sua chamada de ligação do Butterknife deverá se parecer com:

(@OnClick(R.id.my_layout_id) 
public void yourMethod {
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}

Link da documentação do Butterknife: http://jakewharton.github.io/butterknife/

Plug: Butterknife irá revolucionar o seu desenvolvimento Android. Considere isso.

Nota: O mesmo resultado pode ser alcançado sem o uso da biblioteca externa Butterknife. Basta definir um OnClickListener para o layout pai, conforme descrito acima.


Impressionante, solução perfeita! Obrigado.
Nullforlife

6

No kotlin, podemos fazer o seguinte. Não há necessidade de iterar todas as visualizações. Também funcionará para fragmentos.

override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
    currentFocus?.let {
        val imm: InputMethodManager = getSystemService(
            Context.INPUT_METHOD_SERVICE
        ) as (InputMethodManager)
        imm.hideSoftInputFromWindow(it.windowToken, 0)
    }
    return super.dispatchTouchEvent(ev)
}

não funciona a partir de um fragmento
Nurseyit Tursunkulov 12/12/19

isso funciona, mas tem um bug. por exemplo, se eu quiser colar texto na visualização de texto, o teclado oculta e aparece. É um pouco chato.
George Shalvashvili 23/01

Então me diga a melhor solução ..
Sai

4

Aqui está outra variação da resposta do fje que aborda as questões levantadas pelo sosite.

A idéia aqui é lidar com as ações down e up no dispatchTouchEventmétodo da Activity . Na ação descendente, anotamos a visualização atualmente focada (se houver) e se o toque estava dentro dela, salvando essas informações para mais tarde.

Na ação inicial, despachamos primeiro, para permitir que outra visualização potencialmente se concentre. Se depois disso, a visão atualmente focada for a visão originalmente focada e o toque para baixo estiver dentro dessa visão, deixaremos o teclado aberto.

Se a visualização atualmente focada for diferente da visualização originalmente focada e for uma EditText, também deixaremos o teclado aberto.

Caso contrário, fechamos.

Então, para resumir, isso funciona da seguinte maneira:

  • ao tocar dentro de um foco atualmente EditText, o teclado permanece aberto
  • ao passar de um foco EditTextpara outro EditText, o teclado permanece aberto (não fecha / reabre)
  • ao tocar em qualquer lugar fora de um foco atualmente diferente de EditTextoutro EditText, o teclado fecha
  • ao pressionar longamente um EditTextpara exibir a barra de ação contextual (com os botões de recortar / copiar / colar), o teclado permanece aberto, mesmo que a ação UP tenha ocorrido fora do foco EditText(que foi movido para baixo para dar espaço ao CAB) . Observe, porém, que quando você pressiona um botão no CAB, ele fecha o teclado. Isso pode ou não ser desejável; se você deseja cortar / copiar de um campo e colar em outro, seria. Se você deseja colar novamente no mesmo EditText, não seria.
  • quando o foco EditTextestá na parte inferior da tela e você clica prolongadamente em algum texto para selecioná-lo, ele EditTextmantém o foco e, portanto, o teclado abre como você deseja, porque fazemos a verificação "toque está dentro dos limites da vista" na ação para baixo , não a ação up.

    private View focusedViewOnActionDown;
    private boolean touchWasInsideFocusedView;
    
    
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                focusedViewOnActionDown = getCurrentFocus();
                if (focusedViewOnActionDown != null) {
                    final Rect rect = new Rect();
                    final int[] coordinates = new int[2];
    
                    focusedViewOnActionDown.getLocationOnScreen(coordinates);
    
                    rect.set(coordinates[0], coordinates[1],
                            coordinates[0] + focusedViewOnActionDown.getWidth(),
                            coordinates[1] + focusedViewOnActionDown.getHeight());
    
                    final int x = (int) ev.getX();
                    final int y = (int) ev.getY();
    
                    touchWasInsideFocusedView = rect.contains(x, y);
                }
                break;
    
            case MotionEvent.ACTION_UP:
    
                if (focusedViewOnActionDown != null) {
                    // dispatch to allow new view to (potentially) take focus
                    final boolean consumed = super.dispatchTouchEvent(ev);
    
                    final View currentFocus = getCurrentFocus();
    
                    // if the focus is still on the original view and the touch was inside that view,
                    // leave the keyboard open.  Otherwise, if the focus is now on another view and that view
                    // is an EditText, also leave the keyboard open.
                    if (currentFocus.equals(focusedViewOnActionDown)) {
                        if (touchWasInsideFocusedView) {
                            return consumed;
                        }
                    } else if (currentFocus instanceof EditText) {
                        return consumed;
                    }
    
                    // the touch was outside the originally focused view and not inside another EditText,
                    // so close the keyboard
                    InputMethodManager inputMethodManager =
                            (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                    inputMethodManager.hideSoftInputFromWindow(
                        focusedViewOnActionDown.getWindowToken(), 0);
                    focusedViewOnActionDown.clearFocus();
    
                    return consumed;
                }
                break;
        }
    
        return super.dispatchTouchEvent(ev);
    }

4

é muito simples, basta tornar seu layout recente clicável um foco por este código:

android:id="@+id/loginParentLayout"
android:clickable="true"
android:focusableInTouchMode="true"

e, em seguida, escreva um método e um OnClickListner para esse layout, para que quando o layout mais alto for tocado em qualquer lugar em que ele chamará um método no qual você escreverá código para descartar o teclado. a seguir está o código para ambos; // você precisa escrever isso em OnCreate ()

 yourLayout.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View view) {
                    hideKeyboard(view);
                }
            });

método chamado do listner: -

 public void hideKeyboard(View view) {
     InputMethodManager imm =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }

4

Acho que o bit de resposta aceito é complexo para esse requisito simples. Aqui está o que funcionou para mim sem nenhuma falha.

findViewById(R.id.mainLayout).setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
            return false;
        }
    });

11
Se você usa NestedScrollViewlayouts complexos, consulte a resposta aceita: stackoverflow.com/a/11656129/2914140 . Você deve saber que outros contêineres podem consumir toques.
CoolMind 04/02/19

3

Existe uma abordagem mais simples, baseada no mesmo problema do iPhone. Basta substituir o layout do plano de fundo no evento de toque, onde o texto de edição está contido. Basta usar este código no OnCreate da atividade (login_fondo é o layout raiz):

    final LinearLayout llLogin = (LinearLayout)findViewById(R.id.login_fondo);
    llLogin.setOnTouchListener(
            new OnTouchListener()
            {
                @Override
                public boolean onTouch(View view, MotionEvent ev) {
                    InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(
                            android.content.Context.INPUT_METHOD_SERVICE);
                    imm.hideSoftInputFromWindow(mActivity.getCurrentFocus().getWindowToken(), 0);
                    return false;
                }
            });

11
Como eu disse e lembro, isso só funciona se o formulário não estiver dentro de um ScrollView.
Htafoya

11
Isso não funciona muito bem se o layout de segundo plano contiver outros layouts filhos.
AxeEffect

Obrigado por lembrar que onClick não era a única opção.
Harpreet

3

Método para mostrar / ocultar o teclado virtual

InputMethodManager inputMethodManager = (InputMethodManager) currentActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
    if (isShow) {
        if (currentActivity.getCurrentFocus() == null) {
            inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
        } else {
            inputMethodManager.showSoftInput(currentActivity.getCurrentFocus(), InputMethodManager.SHOW_FORCED);    
        }

    } else {
        if (currentActivity.getCurrentFocus() == null) {
            inputMethodManager.toggleSoftInput(InputMethodManager.HIDE_NOT_ALWAYS, 0);
        } else {
            inputMethodManager.hideSoftInputFromInputMethod(currentActivity.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);    
        }

    }

Espero que tenham sido úteis


3

Esta é a solução mais fácil para mim (e elaborada por mim).

Este é o método para ocultar o teclado.

public void hideKeyboard(View view){
        if(!(view instanceof EditText)){
            InputMethodManager inputMethodManager=(InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),0);
        }
    }

agora defina o atributo onclick do layout pai da atividade como o método acima hideKeyboard, na visualização Design do seu arquivo XML ou escrevendo o código abaixo na visualização Texto do seu arquivo XML.

android:onClick="hideKeyboard"

2

Refinei o método, coloque o código a seguir em alguma classe de utilitário da interface do usuário (de preferência, não necessariamente) para que ele possa ser acessado de todas as suas classes Activity ou Fragment para servir a seu propósito.

public static void serachAndHideSoftKeybordFromView(View view, final Activity act) {
    if(!(view instanceof EditText)) {
        view.setOnTouchListener(new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                hideSoftKeyboard(act);
                return false;
            }
        });
    }
    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            View nextViewInHierarchy = ((ViewGroup) view).getChildAt(i);
            serachAndHideSoftKeybordFromView(nextViewInHierarchy, act);
        }
    }
}
public static void hideSoftKeyboard (Activity activity) {
    InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
    inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
}

Então diga, por exemplo, que você precisa chamá-lo de atividade, chame-o da seguinte maneira;

UIutils.serachAndHideSoftKeybordFromView(findViewById(android.R.id.content), YourActivityName.this);

Aviso prévio

findViewById (android.R.id.content)

Isso nos fornece a visualização raiz do grupo atual (você não deve ter definido o ID na visualização raiz).

Felicidades :)



2

Atividade

 @Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
     ScreenUtils.hideKeyboard(this, findViewById(android.R.id.content).getWindowToken());
     return super.dispatchTouchEvent(ev);
 }

ScreenUtils

 public static void hideKeyboard(Context context, IBinder windowToken) {
     InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
     imm.hideSoftInputFromWindow(windowToken, InputMethodManager.HIDE_NOT_ALWAYS);
 }

3
Esse código é simples, mas tem um problema óbvio: fecha o teclado quando qualquer lugar é tocado. Ou seja, se você tocar em um local diferente do EditText para mover o cursor de entrada, ele oculta o teclado e o teclado aparece novamente pelo sistema.
Malditos legumes

2

Basta adicionar este código na classe @Overide

public boolean dispatchTouchEvent(MotionEvent ev) {
    View view = getCurrentFocus();
    if (view != null && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_MOVE) && view instanceof EditText && !view.getClass().getName().startsWith("android.webkit.")) {
        int scrcoords[] = new int[2];
        view.getLocationOnScreen(scrcoords);
        float x = ev.getRawX() + view.getLeft() - scrcoords[0];
        float y = ev.getRawY() + view.getTop() - scrcoords[1];
        if (x < view.getLeft() || x > view.getRight() || y < view.getTop() || y > view.getBottom())
            ((InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow((this.getWindow().getDecorView().getApplicationWindowToken()), 0);
    }
    return super.dispatchTouchEvent(ev);
}

3
Embora isso possa responder à pergunta, é melhor explicar as partes essenciais da resposta e, possivelmente, qual foi o problema com o código do OP.
Pirho

sim @pirho Eu também concordo com você Haseeb precisa se concentrar em dar uma resposta adequada.
Dilip

2
@Dilip Você sabia que também pode votar com antecedência? Isso é apenas para manter a seção de comentários limpa, para que não haja muitos comentários com o mesmo argumento.
Pirho

2

Em vez de iterar por todas as visualizações ou substituir o dispatchTouchEvent.

Por que não apenas substituir o onUserInteraction () da Atividade, isso garantirá que o teclado seja descartado sempre que o usuário tocar fora do EditText.

Funcionará mesmo quando o EditText estiver dentro do scrollView.

@Override
public void onUserInteraction() {
    if (getCurrentFocus() != null) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
    }
}

esta é a melhor resposta
ovluca

Sim! Essa é uma resposta super limpa. E, se você criar uma AbstractActivity que todas as suas outras atividades estendem, poderá incorporá-la como o comportamento padrão em todo o aplicativo.
wildcat12

1

Consegui trabalhar com uma ligeira variante da solução de Fernando Camarago. No meu método onCreate, anexo um único onTouchListener à visualização raiz, mas a envio, em vez da atividade, como argumento.

        findViewById(android.R.id.content).setOnTouchListener(new OnTouchListener() {           
        public boolean onTouch(View v, MotionEvent event) {
            Utils.hideSoftKeyboard(v);
            return false;
        }
    });

Em uma classe Utils separada é ...

    public static void hideSoftKeyboard(View v) {
    InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 
    imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}

1

Isso pode ser antigo, mas eu consegui isso implementando uma classe personalizada

public class DismissKeyboardListener implements OnClickListener {

    Activity mAct;

    public DismissKeyboardListener(Activity act) {
        this.mAct = act;
    }

    @Override
    public void onClick(View v) {
        if ( v instanceof ViewGroup ) {
            hideSoftKeyboard( this.mAct );
        }
    }       
}

public void hideSoftKeyboard(Activity activity) {
        InputMethodManager imm = (InputMethodManager)
        getSystemService(Activity.INPUT_METHOD_SERVICE);
        imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
}

a melhor prática aqui é criar uma classe Helper e todos os layouts relativos / lineares de contêineres devem implementar isso.

**** Observe que apenas o contêiner principal deve implementar esta classe (para otimização) ****

e implementá-lo assim:

Parent.setOnClickListener( new DismissKeyboardListener(this) ); 

a palavra-chave é para Atividade. então, se você estiver no fragmento, use como getActivity ();

--- polegar para cima, se isso ajudá-lo ... --- aplaude Ralph ---


1

Esta é uma versão ligeiramente modificada da resposta de fje que funcionou perfeitamente na maioria das vezes.

Esta versão usa ACTION_DOWN, portanto, executar uma ação de rolagem também fecha o teclado. Também não propaga o evento, a menos que você clique em outro EditText. Isso significa que clicar em qualquer lugar fora do seu EditText, mesmo em outro clicável, simplesmente fecha o teclado.

@Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
    if(ev.getAction() == MotionEvent.ACTION_DOWN)
    {
        final View view = getCurrentFocus();

        if(view != null)
        {
            final View viewTmp = getCurrentFocus();
            final View viewNew = viewTmp != null ? viewTmp : view;

            if(viewNew.equals(view))
            {
                final Rect rect = new Rect();
                final int[] coordinates = new int[2];

                view.getLocationOnScreen(coordinates);

                rect.set(coordinates[0], coordinates[1], coordinates[0] + view.getWidth(), coordinates[1] + view.getHeight());

                final int x = (int) ev.getX();
                final int y = (int) ev.getY();

                if(rect.contains(x, y))
                {
                    super.dispatchTouchEvent(ev);
                    return true;
                }
            }
            else if(viewNew instanceof EditText || viewNew instanceof CustomEditText)
            {
                super.dispatchTouchEvent(ev);
                return true;
            }

            final InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);

            inputMethodManager.hideSoftInputFromWindow(viewNew.getWindowToken(), 0);

            viewNew.clearFocus();

            return true;
        }
    }
    return super.dispatchTouchEvent(ev);
}

Algo não parece bem aqui; você está atribuindo ambos viewe viewTmppara getCurrentFocus(), para que eles sempre tenham o mesmo valor.
Andy Dennie 24/09

1

Eu fiz assim:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
   View view = getCurrentFocus();
   if (view != null && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_MOVE) && view instanceof EditText && !view.getClass().getName().startsWith("android.webkit.")) {
            int scrcoords[] = new int[2];
            view.getLocationOnScreen(scrcoords);
            float x = ev.getRawX() + view.getLeft() - scrcoords[0];
            float y = ev.getRawY() + view.getTop() - scrcoords[1];
            if (x < view.getLeft() || x > view.getRight() || y < view.getTop() || y > view.getBottom())
                hideKeyboard(this);
        }
    return super.dispatchTouchEvent(ev);
}

Ocultar código do teclado :

public static void hideKeyboard(Activity act) {
    if(act!=null)
      ((InputMethodManager)act.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow((act.getWindow().getDecorView().getApplicationWindowToken()), 0);
  }

Feito

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.