Rilevamento quando l'utente ha chiuso la tastiera virtuale


114

Ho un widget EditText nella mia vista. Quando l'utente seleziona il widget EditText, vengono visualizzate alcune istruzioni e viene visualizzata la tastiera virtuale.

Uso un OnEditorActionListener per rilevare quando l'utente ha completato l'immissione di testo e chiudo la tastiera, nascondo le istruzioni ed eseguo alcune azioni.

Il mio problema è quando l'utente chiude la tastiera premendo il tasto BACK. Il sistema operativo chiude la tastiera, ma le mie istruzioni (che devo nascondere) sono ancora visibili.

Ho provato a sovrascrivere OnKeyDown, ma non sembra essere chiamato quando il pulsante BACK viene utilizzato per chiudere la tastiera.

Ho provato a impostare un OnKeyListener sul widget EditText, ma non sembra nemmeno essere chiamato.

Come posso rilevare quando la tastiera virtuale viene chiusa?

Risposte:


160

Conosco un modo per farlo. Sottoclasse EditText e implementa:

@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
  if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
    // Do your thing.
    return true;  // So it is not propagated.
  }
  return super.dispatchKeyEvent(event);
}

Ecco un collegamento su come utilizzare le visualizzazioni personalizzate (per quando si sottoclasse EditText): http://developer.android.com/guide/topics/ui/custom-components.html


2
Ricevo rapporti da utenti Android con tastiere hardware che in qualche modo interferiscono con la pressione dei tasti. Non ho ulteriori informazioni in questo momento.
argento

Ho cercato diverse soluzioni, questa è di gran lunga la migliore!
Friesgaard

10
Aspetta aspetta aspetta, l'ho guardato solo una terza volta - non dovrebbe essere la super chiamata onKeyPreIme? O c'è una ragione particolare per cui non è così?
Erhannis

Sembra utile, tranne quando EditText non può essere sottoclasse (ad esempio in un SearchView). Questo è un problema quando si tenta di nascondere il SearchView se vuoto quando la tastiera viene chiusa. Mi chiedo perché gli Android non si limitano a fornire alcune belle API OSK per questo genere di cose.
tbm

2
@tbm Per ottenere un effetto simile a SearchView, si prega di fare riferimento a stackoverflow.com/questions/9629313/...
Cheok Yan Cheng

124

Jay, la tua soluzione è buona! Grazie :)

public class EditTextBackEvent extends EditText {

    private EditTextImeBackListener mOnImeBack;

    public EditTextBackEvent(Context context) {
        super(context);
    }

    public EditTextBackEvent(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public EditTextBackEvent(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && 
            event.getAction() == KeyEvent.ACTION_UP) {
            if (mOnImeBack != null) 
                mOnImeBack.onImeBack(this, this.getText().toString());
        }
        return super.dispatchKeyEvent(event);
    }

    public void setOnEditTextImeBackListener(EditTextImeBackListener listener) {
        mOnImeBack = listener;
    }

}

public interface EditTextImeBackListener {
    public abstract void onImeBack(EditTextBackEvent ctrl, String text);
}

Qualche motivo particolare che vogliamo rilevare KeyEvent.ACTION_UPanche noi?
Cheok Yan Cheng

2
@CheokYanCheng è perché l'azione dell'utente normalmente dovrebbe avere effetto quando si rilascia il pulsante e non quando si inizia a premerlo.
jayeffkay

3
Assicurati di estendere android.support.v7.widget.AppCompatEditTextper la colorazione.
Sanvywell

Estendi: AppCompatEditTextper androidx
COYG

Grande! Suggerirei solo un miglioramento solo per generare la tua soluzione. Passerei gli argomenti da onKeyPreIme "così com'è" all'ascoltatore, in questo modo puoi implementare la tua logica in diversi modi in cui ti serve.
marcRDZ

17

Ho apportato una leggera modifica alla soluzione di Jay chiamando super.onKeyPreIme ():

_e = new EditText(inflater.getContext()) {
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK){
            cancelTextInput();
        }
        return super.onKeyPreIme(keyCode, event);
    }
};

Soluzione meravigliosa, Jay, +1!


14

Ecco il mio EditText personalizzato per rilevare se la tastiera è visualizzata o meno

/**
 * Created by TheFinestArtist on 9/24/15.
 */
public class KeyboardEditText extends EditText {

    public KeyboardEditText(Context context) {
        super(context);
    }

    public KeyboardEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public KeyboardEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        if (listener != null)
            listener.onStateChanged(this, true);
    }

    @Override
    public boolean onKeyPreIme(int keyCode, @NonNull KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
                && event.getAction() == KeyEvent.ACTION_UP) {
            if (listener != null)
                listener.onStateChanged(this, false);
        }
        return super.onKeyPreIme(keyCode, event);
    }

    /**
     * Keyboard Listener
     */
    KeyboardListener listener;

    public void setOnKeyboardListener(KeyboardListener listener) {
        this.listener = listener;
    }

    public interface KeyboardListener {
        void onStateChanged(KeyboardEditText keyboardEditText, boolean showing);
    }
}

8

È il 2019 ora ...
Quindi ho creato una soluzione più accurata con Kotlin

1.Crea una funzione di estensione:

fun Activity.addKeyboardToggleListener(onKeyboardToggleAction: (shown: Boolean) -> Unit): KeyboardToggleListener? {
    val root = findViewById<View>(android.R.id.content)
    val listener = KeyboardToggleListener(root, onKeyboardToggleAction)
    return root?.viewTreeObserver?.run {
        addOnGlobalLayoutListener(listener)
        listener
    }
}

2.Dove si trova l'ascoltatore di commutazione:

open class KeyboardToggleListener(
        private val root: View?,
        private val onKeyboardToggleAction: (shown: Boolean) -> Unit
) : ViewTreeObserver.OnGlobalLayoutListener {
    private var shown = false
    override fun onGlobalLayout() {
        root?.run {
            val heightDiff = rootView.height - height
            val keyboardShown = heightDiff > dpToPx(200f)
            if (shown != keyboardShown) {
                onKeyboardToggleAction.invoke(keyboardShown)
                shown = keyboardShown
            }
        }
    }
}

fun View.dpToPx(dp: Float) = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics).roundToInt()

3. Usalo in qualsiasi attività semplice come questa :

addKeyboardToggleListener {shown ->
          // hurray! Now you know when the keyboard is shown and hidden!!
      }

Grazie per la soluzione Kotlin. Anche se quando l'ho implementato ho notato che attiva l'ascoltatore più volte per un cambio di tastiera e anche all'avvio. Ho dovuto memorizzare lo stato aperto / non aperto e richiamare gli ascoltatori solo quando il valore era effettivamente diverso.
RandomEngy

@RandomEngy lo ha risolto in KeyboardToggleListener. Grazie per aver notato
Leo Droidcoder

4

Basta creare una classe che estenda Edittext e usare quell'edittext nel tuo codice, dovresti semplicemente sovrascrivere il seguente metodo nell'edittext personalizzato:

@Override
 public boolean onKeyPreIme(int keyCode, KeyEvent event) {
 if (keyCode == KeyEvent.KEYCODE_BACK) {

    //Here it catch all back keys
    //Now you can do what you want.

} else if (keyCode == KeyEvent.KEYCODE_MENU) {
    // Eat the event
    return true;
}
return false;}

C'è un modo per rilevare quando si apre la tastiera?
powder366

3

Ecco una soluzione con l'ascoltatore chiave. Non ho idea del motivo per cui funziona, ma OnKeyListener funziona se sovrascrivi semplicemente onKeyPreIme sul tuo EditText personalizzato.

SomeClass.java

customEditText.setOnKeyListener((v, keyCode, event) -> {
            if(event.getAction() == KeyEvent.ACTION_DOWN) {
                switch (keyCode) {
                    case KeyEvent.KEYCODE_BACK:
                        getPresenter().onBackPressed();
                        break;
                }
            }
            return false;
        }); 

CustomEditText.java

@Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        return super.dispatchKeyEvent(event);
    }

3

Usando la risposta di @ olivier_sdg, ma convertito in Kotlin:

class KeyboardEditText : AppCompatEditText {

    var listener: Listener? = null
  
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
  
    override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean {
        if (event.keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
            listener?.onImeBack(this)
        }
        return super.dispatchKeyEvent(event)
    }
  
    interface Listener {
        fun onImeBack(editText: KeyboardEditText)
    }

}

Uso:

keyboardEditText.listener = object : KeyboardEditText.Listener {
    override fun onImeBack(editText: KeyboardEditText) {
        //Back detected
    }
}

2

Per chiunque desideri fare lo stesso in Xamarin, ho tradotto alcune delle risposte principali in quanto è leggermente diverso. Ho creato una sintesi qui, ma riassumendo, crei un EditText personalizzato e sovrascrivi in ​​questo OnKeyPreImemodo:

public class CustomEditText : EditText
{
    public event EventHandler BackPressed;

    // ...

    public override bool OnKeyPreIme([GeneratedEnum] Keycode keyCode, KeyEvent e)
    {
        if (e.KeyCode == Keycode.Back && e.Action == KeyEventActions.Up)
        {
            BackPressed?.Invoke(this, new EventArgs());
        }

        return base.OnKeyPreIme(keyCode, e);
    }
}

... e poi nella vista ...

editText = FindViewById<CustomEditText>(Resource.Id.MyEditText);
editText.BackPressed += (s, e) => 
{
    // <insert code here>
};

Sebbene sia solo un semplice esempio, consiglierei di non utilizzare metodi anonimi nei gestori di eventi, poiché creano perdite di memoria e molte persone usano gli esempi trovati qui ed eseguono con loro senza rendersene conto. Fonte: docs.microsoft.com/en-us/xamarin/cross-platform/deploy-test/…
Jared

0

hideSoftInputFromWindow restituisce true quando la tastiera si chiude usa il suo valore per rilevare la chiusura della tastiera in Android

InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);

        if (imm.hideSoftInputFromWindow(findFocus().getWindowToken(),
                InputMethodManager.HIDE_NOT_ALWAYS)) {
            //keyboard is closed now do what you need here
        }
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.