Come nascondere la tastiera virtuale su Android dopo aver fatto clic all'esterno di EditText?


354

Ok, tutti sanno che per nascondere una tastiera è necessario implementare:

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

Ma il grosso problema qui è come nascondere la tastiera quando l'utente tocca o seleziona qualsiasi altro posto che non sia una EditTexto la tastiera virtuale?

Ho provato a utilizzare il onTouchEvent()mio genitore, Activityma funziona solo se l'utente tocca al di fuori di qualsiasi altra vista e non c'è nessuna vista di scorrimento.

Ho provato a implementare un touch, click, focus listener senza successo.

Ho anche provato a implementare la mia scrollview per intercettare gli eventi touch, ma posso solo ottenere le coordinate dell'evento e non la vista cliccata.

C'è un modo standard per farlo ?? su iPhone è stato davvero facile.


Bene, ho capito che lo scrollview non era proprio il problema, ma le etichette che c'erano. La vista è un layout verticale con qualcosa come: TextView, EditText, TextView, EditText, ecc. E le textView non permetteranno all'editor di perdere la messa a fuoco e nascondere la tastiera
htafoya,

Puoi trovare una soluzione per getFields()qui: stackoverflow.com/questions/7790487/…
Reto

La tastiera può essere chiusa premendo il pulsante
Invio

4
Ho trovato questa risposta: stackoverflow.com/a/28939113/2610855 La migliore.
Loenix,

Risposte:


575

Il frammento seguente nasconde semplicemente la tastiera:

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

Puoi inserirlo in una classe di utilità o, se lo stai definendo all'interno di un'attività, evita il parametro di attività o chiama hideSoftKeyboard(this).

La parte più difficile è quando chiamarlo. È possibile scrivere un metodo che scorre in ogni Viewattività e verificare se è un instanceof EditTextse non è registrato setOnTouchListenera quel componente e tutto andrà a posto. Nel caso ti stia chiedendo come farlo, in realtà è abbastanza semplice. Ecco cosa fai, scrivi un metodo ricorsivo come il seguente, infatti puoi usarlo per fare qualsiasi cosa, come impostare caratteri personalizzati ecc ... Ecco il metodo

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);
        }
    }
}

Questo è tutto, basta chiamare questo metodo dopo di te setContentViewnella tua attività. Nel caso ti stia chiedendo quale parametro passeresti, è il idcontenitore principale. Assegna un tipo idal tuo contenitore genitore

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

e chiama setupUI(findViewById(R.id.parent)), tutto qui.

Se si desidera utilizzarlo in modo efficace, è possibile creare un Activitymetodo esteso e inserire questo metodo e fare in modo che tutte le altre attività nell'applicazione estendano tale attività e la chiamino setupUI()nel onCreate()metodo.

Spero che sia d'aiuto.

Se si utilizza più di 1 attività, definire un ID comune come layout padre <RelativeLayout android:id="@+id/main_parent"> ... </RelativeLayout>

Quindi estendi una classe da Activitye definisci setupUI(findViewById(R.id.main_parent))All'interno di essa OnResume()ed estendi questa classe invece di `` Attivitàin your program


Ecco una versione di Kotlin della funzione sopra:

@file:JvmName("KeyboardUtils")

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

Non mi sono testato, ma sembra che funzionerebbe e dato che ha recensioni alte cambierò la risposta accettata a questo.
htafoya,

4
Non dovrebbe essere molto difficile? Sono fuori dalla programmazione Android in questo momento, quindi correggimi se sbaglio. Potresti in qualche modo tracciare l'EditText focalizzato in qualsiasi momento e semplicemente richiederlo per perdere il focus durante un OnTouchEvent?
Navneeth G,

25
Non sono sicuro che qualcun altro abbia riscontrato questo problema, ma ciò causa l'arresto anomalo dell'app quando si chiama hideSoftKeyboard se nulla è focalizzato. Puoi risolverlo circondando la seconda riga del metodo conif(activity.getCurrentFocus() != null) {...}
Frank Cangialosi,

14
Il problema con questo approccio è che presume che tutte le altre viste non avranno mai bisogno di impostarle OnTouchListener. Potresti semplicemente impostare quella logica in a ViewGroup.onInterceptTouchEvent(MotionEvent)su una vista radice.
Alex.F,

2
Non funziona quando faccio clic su altri controlli tenendo aperta la tastiera
mohitum

285

È possibile ottenere ciò procedendo come segue:

  1. Rendi la vista padre (vista contenuto della tua attività) cliccabile e focalizzabile aggiungendo i seguenti attributi

        android:clickable="true" 
        android:focusableInTouchMode="true" 
    
  2. Implementare un metodo hideKeyboard ()

        public void hideKeyboard(View view) {
            InputMethodManager inputMethodManager =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
    
  3. Infine, imposta onFocusChangeListener del tuo edittext.

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

Come sottolineato in uno dei commenti seguenti, questo potrebbe non funzionare se la vista padre è una ScrollView. In tal caso, la modalità ClickTable e focusableInTouch può essere aggiunta alla vista direttamente sotto ScrollView.


67
Secondo me questa è la risposta corretta. Meno codice, nessuna iterazione non necessaria ...
gattshjoty,

14
Mi piace molto questa risposta. Una cosa da notare è che questo non ha funzionato per me quando si aggiungono clickablee focusableInTouchModeal mio ScrollViewelemento root . Ho dovuto aggiungere al mio genitore diretto EditTextche era a LinearLayout.
Adam Johns,

10
Ha funzionato perfettamente per me. Tuttavia, se hai due widget edittext, devi assicurarti di gestire correttamente la messa a fuoco di entrambi, altrimenti dovrai nascondere la tastiera inutilmente.
Marka A

1
@MarkaA Non ho avuto problemi con questo, quando faccio clic sull'altro EditText, la tastiera rimane, se si fa clic sullo sfondo si nasconde come dovrebbe. Ho avuto qualche altra gestione su onFocusChange, cambiando solo lo sfondo dell'EditText quando è attivo, niente fence.
CularBytes,

3
@Shrikant - Stavo vedendo anche sfarfallio con più testi di modifica. Ho appena impostato onFocusChangeListener sul genitore anziché su ogni testo di modifica e ho cambiato la condizione per dire se (hasFocus) {hideKeyboard (v); } Non ho più notato sfarfallio che cambiano i testi di modifica btwn.
Manisha,

69

Sostituisci il codice qui sotto in Attività

 @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
Soluzione semplice, aggiungi attività e si occuperà anche del frammento
Rohit Maurya

1
Un'altra soluzione è creare BaseActivity ed estenderlo in tutte le attività
suona sonawane il

1
Soluzione brillante
Ahmed Adel Ismail

1
mi hai salvato dallo sfarfallio dello schermo quando ho chiuso la tastiera. Pollice su!
Dimas Mendes,

1
Bello, ma era troppo bello per essere vero: semplice, molto breve, e ha funzionato ... ahimè, c'è un problema: quando viene visualizzata la tastiera, ogni volta che tocchiamo l'EditText che ha chiesto alla tastiera, scende e automaticamente.
Chrysotribax,

60

Trovo la risposta accettata un po 'complicata.

Ecco la mia soluzione Aggiungi un OnTouchListeneral tuo layout principale, ad es .:

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

e inserisci il seguente codice nel metodo onTouch.

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

In questo modo non è necessario ripetere tutte le visualizzazioni.


@roepit - sto ottenendo un classCastexception per aver provato a lanciare un layout in una vista. mi sto perdendo qualcosa?
Katzenhut,

Puoi fare riferimento al tuo codice da qualche parte? Non posso dire cosa c'è che non va quando non riesco a guardare il tuo layout e il tuo codice attività / frammento eccetera.
Roepit

La migliore risposta là fuori, ancora cercando di avvolgere la mia testa su come funziona.
user40797,

funzionerà se fai clic sulla barra del titolo dell'app?
user1506104,

1
Funziona perfettamente per nascondere la tastiera! Si noti che questo in realtà non sfoca il EditText, nasconde solo la tastiera. Per sfocare anche EditText, aggiungi ad esempio android:onClick="stealFocusFromEditTexts"al file XML della vista padre e quindi public void stealFocusFromEditTexts(View view) {}alla sua attività. Il metodo on-click non deve fare nulla, deve solo esistere affinché la vista padre sia focalizzabile / selezionabile, il che è necessario per rubare l'attenzione dal bambino EditText
Jacob R

40

Ho un'altra soluzione per nascondere la tastiera:

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

Qui passa HIDE_IMPLICIT_ONLYnella posizione di showFlage 0nella posizione di hiddenFlag. Chiuderà forzatamente la tastiera software.


3
Grazie funziona ..... soprattutto avevo provato ma non funziona mentre sto ottenendo valore dalla finestra di dialogo editext text nd closoing dialog box ...
PankajAndroid

Grazie, funziona solo ed è molto più pulito dell'altro sopra! +1
Edmond Tamas,

Funziona come previsto, grazie per la tua soluzione +1
tryp

Funziona come un incanto per me. +1 per una soluzione elegante.
saintjab,

2
scusate, ma questo metodo è attivo, quindi se lo stato della tastiera è già chiuso, mostrerà la tastiera
HendraWD

16

Bene riesco a risolvere un po 'il problema, ho scavalcato il dispatchTouchEvent sulla mia attività, lì sto usando il seguente per nascondere la tastiera.

 /**
 * 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: il metodo getFields () è solo un metodo che restituisce un array con i campi di testo nella vista. Per evitare di creare questo array ad ogni tocco, ho creato un array statico chiamato sFields, che viene restituito con il metodo getFields (). Questo array è inizializzato sui metodi onStart () come:

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


Non è perfetto, il tempo dell'evento di trascinamento si basa solo sull'euristica, quindi a volte non si nasconde quando si eseguono clics lunghi, e ho anche finito creando un metodo per ottenere tutti i editTexts per vista; altrimenti la tastiera si nasconderebbe e mostrerebbe quando si fa clic su altri EditText.

Sono comunque benvenute soluzioni più pulite e più brevi


8
Per aiutare gli altri in futuro, valuteresti di modificare il codice nella tua risposta per includere il tuo getFields()metodo? Non deve essere esatto, solo un esempio con forse solo alcuni commenti che indicano che restituisce una matrice di EditTextoggetti.
Squonk,

14

Usa OnFocusChangeListener .

Per esempio:

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

Aggiornamento : puoi anche ignorare la onTouchEvent()tua attività e controllare le coordinate del tocco. Se le coordinate non rientrano in EditText, nascondi la tastiera.


9
Il problema è che l'edittext non perde la messa a fuoco quando faccio clic su un'etichetta o su altre viste non focalizzabili.
htafoya,

In questo caso ho un'altra soluzione. Ho aggiornato la risposta.
Sergey Glotov,

2
onTouchEvent ha chiamato molte volte, quindi neanche questa è una buona pratica
Jesus Dimrix,

13

Ho implementato dispatchTouchEvent in Activity per fare questo:

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 l'ho provato, funziona perfettamente!


funziona, ma il problema in questo è che se abbiamo più di un EditText, allora dobbiamo considerare anche quello, ma mi è piaciuta la tua risposta :-)
Lalit Poptani,

getActionMasked (ev) è deprecato, quindi ora usa: final int action = ev.getActionMasked (); per la prima linea.
Andrew

12

Un modo più Kotlin e Material Design usando TextInputEditText (questo approccio è anche compatibile con EditTextView ) ...

1.Rendi cliccabile e focalizzabile la vista principale (vista contenuto della tua attività / frammento) aggiungendo i seguenti attributi

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

2. Creare un'estensione per tutte le visualizzazioni (ad esempio all'interno di un file ViewExtension.kt):

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

3.Creare un BaseTextInputEditText che eredita da TextInputEditText. Implementa il metodo onFocusChanged per nascondere la tastiera quando la vista non è focalizzata:

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 chiamare la tua nuovissima vista personalizzata nel tuo 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> 

È tutto. Non è necessario modificare i controller (frammento o attività) per gestire questo caso ripetitivo.


Sì, buono, ma vorrei che ci fosse un modo più semplice!
devDeejay,

11

Sostituisci il dispatch booleano pubblico TouchEvent (evento MotionEvent) in qualsiasi attività (o estendi la classe di attività)

@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 questo è tutto ciò che devi fare


questo è stato il modo più semplice che ho trovato per farlo funzionare. funziona con più EditTexts e ScrollView
RJH

Testato con più EditTexts; Funziona! L'unico svantaggio è che quando si esegue un movimento di trascinamento si nasconde anche.
RominaV,

9

Ho modificato la soluzione di Andre Luis IM Ho raggiunto questo:

Ho creato un metodo di utilità per nascondere la tastiera virtuale allo stesso modo di Andre Luiz IM:

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

Ma invece di registrare un OnTouchListener per ogni vista, che offre prestazioni scadenti, ho registrato OnTouchListener solo per la vista principale. Poiché l'evento bolle fino a quando non viene consumato (EditText è una delle viste che lo consumano per impostazione predefinita), se arriva alla vista principale, è perché non è stato consumato, quindi chiudo la tastiera virtuale.

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

1
Questo sembra essere più sicuro per me.
superarts.org

9

Sono consapevole che questo thread è piuttosto vecchio, la risposta corretta sembra valida e ci sono molte soluzioni funzionanti là fuori, ma penso che l'approccio indicato di seguito potrebbe avere un ulteriore vantaggio in termini di efficienza ed eleganza.

Ho bisogno di questo comportamento per tutte le mie attività, quindi ho creato una classe CustomActivity che eredita dalla classe Activity e ho "agganciato" la funzione dispatchTouchEvent . Ci sono principalmente due condizioni di cui occuparsi:

  1. Se lo stato attivo è invariato e qualcuno sta toccando al di fuori del campo di input corrente, quindi chiudere l'IME
  2. Se lo stato attivo è cambiato e il successivo elemento attivo non è un'istanza di alcun tipo di campo di input, quindi chiudere l'IME

Questo è il mio risultato:

@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 a margine: Inoltre assegno questi attributi alla vista radice, rendendo possibile chiarire lo stato attivo su ogni campo di input e impedendo ai campi di input di focalizzarsi sull'avvio dell'attività (rendendo il contenuto il "focus catcher"):

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

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

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

Super funziona bene grazie !! ho messo un voto per la tua risposta.
venerdì

2
Penso che sia la soluzione migliore per layout complessi. Ma finora ho riscontrato 2 svantaggi: 1. Il menu di scelta rapida di EditText non è selezionabile: qualsiasi clic su di esso causa la perdita di attenzione da EditText 2. Quando il nostro EditText è in fondo a una vista e facciamo clic a lungo su di esso (per selezionare la parola), quindi dopo tastiera mostrata il nostro "punto di clic" è sulla tastiera, non su EditText - quindi perdiamo di nuovo la concentrazione: /
sosite

@sosite, penso di aver affrontato tali limiti nella mia risposta; guarda.
Andy Dennie,

6

Mi è piaciuto l'approccio della chiamata dispatchTouchEventfatto da htafoya, ma:

  • Non ho capito la parte del timer (non so perché misurare i tempi di inattività dovrebbe essere necessario?)
  • Non mi piace registrare / annullare la registrazione di tutti gli EditTexts ad ogni cambio di vista (potrebbero esserci parecchi cambi di vista ed edittex in gerarchie complesse)

Quindi, ho reso questa soluzione un po 'più semplice:

@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());
}

C'è uno svantaggio:

Passare da uno EditTextall'altro EditTextrende la tastiera nascosta e reshow - nel mio caso è desiderata in questo modo, perché mostra che hai commutato tra due componenti di input.


Questo metodo ha funzionato al meglio in termini di capacità di plug-and-play con il mio FragmentActivity.
BillyRayCyrus,

Grazie, è il modo migliore! Ho appena aggiunto anche il controllo dell'azione evento: int action = ev.getActionMasked (); if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) {...}
sergey.n

Grazie ! Mi hai risparmiato tempo ... Migliore risposta.
I.d007,

6

Motivo: riconosco di non avere influenza, ma per favore prendi sul serio la mia risposta.

Problema: ignorare la tastiera virtuale quando si fa clic lontano dalla tastiera o si modifica il testo con un codice minimo.

Soluzione: libreria esterna nota come Butterknife.

Una soluzione di linea:

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

Soluzione più leggibile:

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

Spiegazione: Associare OnClick Listener all'ID principale del layout XML dell'attività, in modo che qualsiasi clic sul layout (non sul testo o sulla tastiera di modifica) eseguirà quel frammento di codice che nasconderà la tastiera.

Esempio: se il tuo file di layout è R.layout.my_layout e il tuo ID di layout è R.id.my_layout_id, la tua chiamata di bind Butterknife dovrebbe apparire come:

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

Link alla documentazione di Butterknife: http://jakewharton.github.io/butterknife/

Plug: Butterknife rivoluzionerà il tuo sviluppo Android. Tieni conto di questo.

Nota: lo stesso risultato può essere ottenuto senza l'uso della libreria esterna Butterknife. Basta impostare un OnClickListener sul layout principale come descritto sopra.


Soluzione fantastica e perfetta! Grazie.
nullforlife,

6

In kotlin, possiamo fare quanto segue. Non è necessario ripetere tutte le visualizzazioni. Funzionerà anche per i frammenti.

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)
}

non funziona dal frammento
Nurseyit Tursunkulov,

funziona ma ha un bug. ad esempio, se voglio incollare il testo nella visualizzazione di testo, la tastiera si nasconde e quindi si presenta. È un po 'fastidioso.
George Shalvashvili,

Allora dimmi la soluzione migliore ..
Sai

4

Ecco un'altra variante della risposta di Fje che affronta i problemi sollevati da sosite.

L'idea qui è di gestire sia le azioni down che up nel dispatchTouchEventmetodo Activity . Nell'azione in basso, prendiamo nota della vista attualmente focalizzata (se presente) e se il tocco era al suo interno, salvando entrambi quei frammenti di informazioni per dopo.

Per quanto riguarda l'azione in alto, per prima cosa inviamo, per consentire a un'altra vista di concentrarsi potenzialmente. Se dopo ciò, la vista attualmente focalizzata è la vista originariamente focalizzata e il tocco verso il basso era all'interno di quella vista, allora lasciamo aperta la tastiera.

Se la vista attualmente focalizzata è diversa dalla vista focalizzata originariamente ed è una EditText, allora lasciamo anche la tastiera aperta.

Altrimenti lo chiudiamo.

Quindi, per riassumere, questo funziona come segue:

  • quando si tocca all'interno di un focus attualmente EditText, la tastiera rimane aperta
  • quando si passa da una messa a fuoco EditTextall'altra EditText, la tastiera rimane aperta (non si chiude / riapre)
  • quando si tocca un punto al di fuori di un focus attualmente EditTextnon diverso EditText, la tastiera si chiude
  • quando si preme a lungo in un EditTextper richiamare la barra di azione contestuale (con i pulsanti taglia / copia / incolla), la tastiera rimane aperta, anche se l'azione SU ha avuto luogo al di fuori del focus EditText(che si è spostato verso il basso per fare spazio al CAB) . Si noti, tuttavia, che quando si tocca un pulsante nel CAB, si chiuderà la tastiera. Ciò può essere o non essere desiderabile; se si desidera tagliare / copiare da un campo e incollarlo in un altro, sarebbe. Se si desidera incollare nuovamente nello stesso EditText, non lo sarebbe.
  • quando la messa a fuoco EditTextè nella parte inferiore dello schermo e fai clic a lungo su un po 'di testo per selezionarlo, EditTextmantiene la messa a fuoco e quindi la tastiera si apre come desideri, perché facciamo il controllo "tocca è all'interno dei limiti della vista" sull'azione in basso , non l'azione in alto.

    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

è troppo semplice, basta rendere selezionabile il layout recente selezionabile da questo codice:

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

e quindi scrivere un metodo e un OnClickListner per quel layout, in modo che quando si tocca il layout più in alto in qualsiasi punto chiamerà un metodo in cui si scriverà il codice per chiudere la tastiera. di seguito è riportato il codice per entrambi; // devi scrivere questo in OnCreate ()

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

metodo chiamato dal listner: -

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

4

Trovo il bit di risposta accettato complesso per questo semplice requisito. Ecco cosa ha funzionato per me senza alcun problema tecnico.

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;
        }
    });

1
Se si utilizzano NestedScrollViewlayout complessi o, vedere la risposta accettata: stackoverflow.com/a/11656129/2914140 . Dovresti sapere che altri contenitori possono consumare tocchi.
CoolMind

3

C'è un approccio più semplice, basato sullo stesso problema di iPhone. Sostituisci semplicemente il layout dello sfondo all'evento touch, in cui è contenuto il testo di modifica. Basta usare questo codice in OnCreate dell'attività (login_fondo è il layout di root):

    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;
                }
            });

1
Come ho detto e ricordo, questo funziona solo se il modulo non si trova all'interno di ScrollView.
htafoya,

1
Questo non funziona molto bene se il layout di sfondo contiene altri layout secondari.
AxeEffect,

Grazie per aver ricordato che onClick non era l'unica opzione.
Harpreet,

3

Metodo per mostrare / nascondere la tastiera software

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);    
        }

    }

Spero siano stati utili


3

Questa è la soluzione più semplice per me (e elaborata da me).

Questo è il metodo per nascondere la tastiera.

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

ora imposta l'attributo onclick del layout principale dell'attività sul metodo sopra hideKeyboardo dalla vista Progettazione del tuo file XML o scrivendo il codice sottostante nella vista Testo del tuo file XML.

android:onClick="hideKeyboard"

2

Ho perfezionato il metodo, ho inserito il seguente codice in alcune classi di utilità dell'interfaccia utente (preferibilmente, non necessariamente) in modo che sia possibile accedervi da tutte le classi Activity o Fragment per servire al suo scopo.

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);
}

Quindi dì ad esempio che devi chiamarlo dall'attività, chiamalo come segue;

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

Avviso

findViewById (android.R.id.content)

Questo ci dà la vista principale del gruppo corrente (non devi aver impostato l'id sulla vista principale).

Saluti :)



2

Attività

 @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
Questo codice è semplice, ma ha un problema evidente: chiude la tastiera quando viene toccato ovunque . Cioè se tocchi una diversa posizione di EditText per spostare il cursore di input, nasconde la tastiera e la tastiera si apre di nuovo dal sistema.
Damn Vegetables,

2

Aggiungi questo codice nella 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
Sebbene ciò possa rispondere alla domanda, è meglio spiegare le parti essenziali della risposta e probabilmente qual è stato il problema con il codice OP.
Pirho,

si @pirho Sono anche d'accordo con te Haseeb deve concentrarsi sul dare una risposta adeguata.
Dilip

2
@Dilip Lo sapevi che puoi anche votare i commenti che accetti? Questo è solo per mantenere pulita la sezione dei commenti in modo da non avere molti commenti che hanno effettivamente lo stesso punto.
Pirho,

2

Invece di scorrere tutte le visualizzazioni o ignorare dispatchTouchEvent.

Perché non semplicemente sovrascrivere onUserInteraction () dell'Attività questo farà in modo che la tastiera venga chiusa ogni volta che l'utente tocca al di fuori di EditText.

Funzionerà anche quando EditText è all'interno di scrollView.

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

questa è la risposta migliore
ovluca

Sì! Questa è una risposta super pulita. E se crei un'Attività astratta estesa a tutte le altre attività, puoi attivarla come comportamento predefinito in tutta la tua app.
wildcat12

1

Ho funzionato con una leggera variante della soluzione di Fernando Camarago. Nel mio metodo onCreate allego un singolo onTouchListener alla vista radice ma invio la vista anziché l'attività come argomento.

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

In una classe Utils separata è ...

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

1

Potrebbe essere vecchio, ma ho funzionato impiantando una classe personalizzata

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);
}

la migliore pratica qui è quella di creare una classe Helper e ogni contenitore relativo / Layout lineare dovrebbe implementarlo.

**** Prendi nota che solo il contenitore principale deve implementare questa classe (per ottimizzazione) ****

e implementarlo in questo modo:

Parent.setOnClickListener( new DismissKeyboardListener(this) ); 

la parola chiave è per Attività. quindi se sei su un frammento usi come getActivity ();

--- pollice in alto se ti aiuta ... --- saluti Ralph ---


1

Questa è una versione leggermente modificata della risposta di Fje che per lo più ha funzionato perfettamente.

Questa versione utilizza ACTION_DOWN quindi l'esecuzione di un'azione di scorrimento chiude anche la tastiera. Inoltre non propaga l'evento a meno che non si faccia clic su un altro EditText. Ciò significa che facendo clic ovunque all'esterno di EditText, anche su un altro clic, si chiude semplicemente la tastiera.

@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);
}

Qualcosa non sembra proprio qui; stai assegnando entrambi viewe viewTmpa getCurrentFocus(), quindi avranno sempre lo stesso valore.
Andy Dennie,

1

Ho fatto così:

@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);
}

Nascondi codice tastiera :

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

Fatto

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.