Détecter le moment où l'utilisateur a fermé le clavier virtuel


114

J'ai un widget EditText dans ma vue. Lorsque l'utilisateur sélectionne le widget EditText, j'affiche des instructions et le clavier virtuel apparaît.

J'utilise un OnEditorActionListener pour détecter quand l'utilisateur a terminé la saisie de texte et je ferme le clavier, cache les instructions et effectue une action.

Mon problème est lorsque l'utilisateur ferme le clavier en appuyant sur la touche RETOUR. Le système d'exploitation rejette le clavier, mais mes instructions (que je dois cacher) sont toujours visibles.

J'ai essayé de remplacer OnKeyDown, mais cela ne semble pas être appelé lorsque le bouton RETOUR est utilisé pour ignorer le clavier.

J'ai essayé de définir un OnKeyListener sur le widget EditText, mais cela ne semble pas non plus être appelé.

Comment puis-je détecter la fermeture du clavier virtuel?

Réponses:


160

Je connais un moyen de faire ça. Sous-classez le EditText et implémentez:

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

Voici un lien sur la façon d'utiliser vos vues personnalisées (lorsque vous sous-classez EditText): http://developer.android.com/guide/topics/ui/custom-components.html


2
Je reçois des rapports d'utilisateurs d'Android avec des claviers matériels indiquant que cela interfère d'une manière ou d'une autre avec les touches. Je n'ai pas d'informations supplémentaires pour le moment.
esilver

J'ai cherché plusieurs solutions, c'est de loin la meilleure!
Friesgaard

10
Attendez, attendez, je viens de regarder cela une troisième fois - le super appel ne devrait-il pas être fait onKeyPreIme? Ou y a-t-il une raison particulière pour qu'il n'en soit pas ainsi?
Erhannis

Cela semble utile, sauf là où le EditText ne peut pas être sous-classé (par exemple dans un SearchView). Il s'agit d'un problème lorsque vous essayez de masquer SearchView s'il est vide lorsque le clavier est fermé. Je me demande pourquoi les gens d'Android ne fournissent pas seulement de belles API OSK pour ce genre de choses.
tbm

2
@tbm Pour obtenir un effet similaire en SearchView, veuillez vous référer à stackoverflow.com/questions/9629313/...
Cheok Yan Cheng

124

Jay, votre solution est bonne! Merci :)

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

Une raison particulière que nous voulons détecter KeyEvent.ACTION_UPégalement?
Cheok Yan Cheng

2
@CheokYanCheng c'est parce que l'action de l'utilisateur doit normalement prendre effet lors du relâchement du bouton, et non au moment de commencer à appuyer dessus.
jayeffkay

3
Assurez-vous de prolonger android.support.v7.widget.AppCompatEditTextpour la teinture.
Sanvywell

Prolonger: AppCompatEditTextpour androidx
COYG

Génial! Je suggérerais seulement une amélioration juste pour générer votre solution. Je passerais les arguments de onKeyPreIme "tels quels" à l'auditeur, de cette façon, vous pouvez implémenter votre logique de différentes manières là où vous en avez besoin.
marcRDZ le

17

J'ai fait un léger changement sur la solution de Jay en appelant 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);
    }
};

Magnifique solution, Jay, +1!


14

Voici mon EditText personnalisé pour détecter si le clavier est affiché ou non

/**
 * 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


Nous sommes en 2019 maintenant ... J'ai donc créé une solution plus soignée avec Kotlin

1.Créez une fonction d'extension:

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

Où se trouve l'écouteur à bascule:

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. Utilisez-le dans n'importe quelle activité aussi simple que celle-ci :

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

Merci pour la solution Kotlin. Cependant, lorsque je l'ai implémenté, j'ai remarqué qu'il déclenche l'auditeur plusieurs fois pour un changement de clavier, ainsi qu'au démarrage. Je devais stocker l'état ouvert / non ouvert et n'appeler les écouteurs que lorsque la valeur était réellement différente.
RandomEngy

@RandomEngy l'a corrigé dans KeyboardToggleListener. Merci d'avoir remarqué
Leo Droidcoder

4

Créez simplement une classe qui étend Edittext et utilisez cet edittext dans votre code, vous devez simplement remplacer la méthode suivante dans le edittext personnalisé:

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

Existe-t-il un moyen de détecter l'ouverture du clavier?
poudre366

3

Voici une solution avec l'auditeur clé. Je n'ai aucune idée de pourquoi cela fonctionne mais OnKeyListener fonctionne si vous remplacez simplement onKeyPreIme sur votre EditText personnalisé.

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

En utilisant la réponse de @ olivier_sdg, mais convertie en 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)
    }

}

Usage:

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

2

Pour tous ceux qui cherchent à faire de même dans Xamarin, j'ai traduit certaines des principales réponses car elles sont un peu différentes. J'ai créé un résumé ici, mais en résumé, vous créez un EditText personnalisé et remplacez OnKeyPreImecomme suit :

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

... puis dans la vue ...

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

Bien que ce ne soit qu'un exemple simple, je recommanderais de ne pas utiliser de méthodes anonymes dans les gestionnaires d'événements, car ceux-ci créent des fuites de mémoire, et de nombreuses personnes utilisent les exemples trouvés ici et exécutent avec eux sans s'en rendre compte. Source: docs.microsoft.com/en-us/xamarin/cross-platform/deploy-test/…
Jared

0

hideSoftInputFromWindow renvoie true lorsque le clavier se ferme, utilisez sa valeur pour détecter la fermeture du clavier dans 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
        }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.