Événement clé de suppression (retour arrière) Android EditText

122

Comment puis-je détecter un événement clé de suppression (retour arrière) pour un editText? J'ai essayé d'utiliser TextWatcher, mais lorsque le editText est vide, lorsque j'appuie sur la touche Suppr, rien ne se passe. Je veux détecter la touche de suppression appuyée sur un editText même s'il n'a pas de texte.

Buda Gavril
la source

Réponses:

173

REMARQUE: onKeyListenerne fonctionne pas pour les claviers logiciels.

Vous pouvez définir OnKeyListenerpour vous editTextafin que vous puissiez détecter toute appuyer sur la touche
EDIT: Une erreur commune que nous vérifions KeyEvent.KEYCODE_BACKpour backspace, mais vraiment il est KeyEvent.KEYCODE_DEL(vraiment ce nom est très déroutant!)

editText.setOnKeyListener(new OnKeyListener() {                 
    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        //You can identify which key pressed buy checking keyCode value with KeyEvent.KEYCODE_
        if(keyCode == KeyEvent.KEYCODE_DEL) {  
            //this is for backspace
        }
        return false;       
    }
});
Labeeb Panampullan
la source
9
Je viens de l'essayer, mais onKeyListeners n'enregistre apparemment pas les backspaces.
stefs
3
Cela ne fonctionnera pas pour le clavier logiciel. Cela ne fonctionnera que pour l'entrée matérielle.
Varundroid
6
Sur mon Nexus4 ( en cours d' exécution stock KitKat) ce fait le travail pour le clavier logiciel.
Matthias
10
SO si cela ne fonctionne pas pour les touches programmables, alors pourquoi cette réponse est-elle acceptée dans / sous la plate-forme Android ..
DJphy
33
à utiliser event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_DELsi vous ne voulez pas que l'événement tire deux fois par pression de retour arrière
Fonix
83

Cela fait un moment que vous n'avez pas posé la question, mais j'ai juste eu le même problème. Comme déjà mentionné par Estel, le problème avec les auditeurs clés est qu'ils ne fonctionnent qu'avec des claviers matériels. Pour ce faire avec un IME (soft keyboard) , la solution est un peu plus élaborée.

La seule méthode que nous voulons remplacer est sendKeyEventdans la classe EditText's InputConnection. Cette méthode est appelée lorsque des événements clés se produisent dans un IME. Mais pour remplacer cela, nous devons implémenter unEditText qui remplace la onCreateInputConnectionméthode, enveloppant l' InputConnectionobjet par défaut dans une classe proxy! : |

Cela semble compliqué, mais voici l'exemple le plus simple que je puisse trouver:

public class ZanyEditText extends EditText {

    private Random r = new Random();

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

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

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

    public void setRandomBackgroundColor() {
        setBackgroundColor(Color.rgb(r.nextInt(256), r.nextInt(256), r
                .nextInt(256)));
    }

    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        return new ZanyInputConnection(super.onCreateInputConnection(outAttrs),
                true);
    }

    private class ZanyInputConnection extends InputConnectionWrapper {

        public ZanyInputConnection(InputConnection target, boolean mutable) {
            super(target, mutable);
        }

        @Override
        public boolean sendKeyEvent(KeyEvent event) {
            if (event.getAction() == KeyEvent.ACTION_DOWN
                    && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
                ZanyEditText.this.setRandomBackgroundColor();
                // Un-comment if you wish to cancel the backspace:
                // return false;
            }
            return super.sendKeyEvent(event);
        }

    }

}

La ligne avec l'appel à setRandomBackgroundColor est l'endroit où mon action spéciale de retour arrière se produit. Dans ce cas, changer la EditTextcouleur d'arrière-plan de.

Si vous gonflez cela à partir de XML, n'oubliez pas d'utiliser le nom complet du package comme balise:

<cc.buttfu.test.ZanyEditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/somefield"
></cc.buttfu.test.ZanyEditText>
Idris
la source
27
J'ai récemment rencontré le même problème sur Jelly Bean. J'ai trouvé que cette solution fonctionnait principalement, sauf que je devais remplacer deleteSurroundingText (...) au lieu de sendKeyEvent (...) (qui n'était pas du tout appelée). J'espère que ceci aide quelqu'un d'autre!
Brandon le
Cette réponse, combinée au commentaire de @Brandon ci-dessus, a fonctionné pour moi. Ce que je me demande maintenant, c'est comment cela fonctionnera sur les appareils pré-JellyBean.
Christopher Perry
Cela fonctionne avec la réponse acceptée sur les appareils 2.2 et 2.3 pour moi.
Christoph
semble déclencher l'événement clé pour le retour arrière deux fois sur 2.3 ...: /
Jeff
25
Cela ne fonctionne pas lorsque l'edittext est vide, des idées sur la façon d'obtenir un événement pour la touche de suppression lorsque l'edittext est vide et n'a pas de texte? 4.2
Rickster
69

Ce n'est qu'un ajout à la réponse d'Idris, ajoutant également le remplacement à deleteSurroundingText. J'ai trouvé plus d'informations à ce sujet ici: Android: Backspace dans WebView / BaseInputConnection

package com.elavon.virtualmerchantmobile.utils;

import java.util.Random;

import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;
import android.widget.EditText;

public class ZanyEditText extends EditText {

    private Random r = new Random();

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

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

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

    public void setRandomBackgroundColor() {
        setBackgroundColor(Color.rgb(r.nextInt(256), r.nextInt(256), r
                .nextInt(256)));
    }

    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        return new ZanyInputConnection(super.onCreateInputConnection(outAttrs),
                true);
    }

    private class ZanyInputConnection extends InputConnectionWrapper {

        public ZanyInputConnection(InputConnection target, boolean mutable) {
            super(target, mutable);
        }

        @Override
        public boolean sendKeyEvent(KeyEvent event) {
            if (event.getAction() == KeyEvent.ACTION_DOWN
                    && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
                ZanyEditText.this.setRandomBackgroundColor();
                // Un-comment if you wish to cancel the backspace:
                // return false;
            }
            return super.sendKeyEvent(event);
        }


        @Override
        public boolean deleteSurroundingText(int beforeLength, int afterLength) {       
            // magic: in latest Android, deleteSurroundingText(1, 0) will be called for backspace
            if (beforeLength == 1 && afterLength == 0) {
                // backspace
                return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
                    && sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
            }

            return super.deleteSurroundingText(beforeLength, afterLength);
        }

    }

}
Jeff
la source
3
Je vous remercie! Le deleteSurroundingTextbit était exactement ce dont j'avais besoin après avoir essayé d'innombrables autres solutions.
Adam Rosenfield
5
Cette solution a très bien fonctionné pour moi sur les versions précédentes d'Android, mais malheureusement, deleteSurroundingText n'est appelé que lors de la suppression des espaces sur 4.4 (KitKat). J'ai testé sur Nexus4 et 7.
Dean
1
il semble que deleteSurroundingText soit requis lorsque EditText est multiligne. Bizarre
Alex Sorokoletov
7
Merci un homme de tonne, n'a pas fonctionné sans deleteSurroundText. Android est si aléatoire qu'ils devraient le renommer en androm.
Torsten Ojaperv
2
Cela fonctionne pour moi, mais je ne peux plus supprimer la ponctuation ou les espaces!
jaytj95
29

Voici ma solution simple, qui fonctionne pour toutes les API:

private int previousLength;
private boolean backSpace;

// ...

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    previousLength = s.length();
}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}

@Override
public void afterTextChanged(Editable s) {
    backSpace = previousLength > s.length();

    if (backSpace) {

        // do your stuff ...

    } 
}

MISE À JOUR 17.04.18 .
Comme indiqué dans les commentaires, cette solution ne suit pas la presse de retour arrière si EditText est vide (comme la plupart des autres solutions).
Cependant, c'est suffisant pour la plupart des cas d'utilisation.
PS Si je devais créer quelque chose de similaire aujourd'hui, je ferais:

public abstract class TextWatcherExtended implements TextWatcher {

    private int lastLength;

    public abstract void afterTextChanged(Editable s, boolean backSpace);

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        lastLength = s.length();
    }

    @Override
    public void afterTextChanged(Editable s) {
        afterTextChanged(s, lastLength > s.length());
    }  
}

Ensuite, utilisez-le simplement comme TextWatcher normal:

 editText.addTextChangedListener(new TextWatcherExtended() {
        @Override
        public void afterTextChanged(Editable s, boolean backSpace) {
           // Here you are! You got missing "backSpace" flag
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            // Do something useful if you wish.
            // Or override it in TextWatcherExtended class if want to avoid it here 
        }
    });
Leo Droidcoder
la source
Juste ce que j'ai besoin! Je vous remercie!
DH28
9
Le TextWatcher ne se déclenche pas sur un EditText vide
Dan Neacșu
@Leo Droidcoder que j'ai utilisé dans une solution similaire. Clair, concis et fonctionne parfaitement ... bravo.
AJW
cet algorithme a un défaut comme si vous cliquez sur l'espace après la saisie, la longueur précédente est supérieure à la longueur
Marcin S.
2
Fonctionne tant que vous n'utilisez pas la sélection (saisie
semi-
13

J'ai envoyé 2 jours pour trouver une solution et j'ai trouvé une solution fonctionnelle :) (sur les touches programmables)

public TextWatcher textWatcher = new TextWatcher() {
@Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {   } 

@Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        if (count == 0) {
        //Put your code here.
        //Runs when delete/backspace pressed on soft key (tested on htc m8)
        //You can use EditText.getText().length() to make if statements here
        }
    }

@Override
    public void afterTextChanged(Editable s) {
    }
}

Après avoir ajouté le textwatcher à votre EditText:

yourEditText.addTextChangedListener(textWatcher);

J'espère que cela fonctionne également sur d'autres appareils Android (Samsung, LG, etc.).

JP
la source
Appareil HTC désir (HTC est commun cependant :-P)
Junaid
si vous
avez
Brilliant no 1 answer bro :)
Gundu Bandgar
6
Cela ne fonctionne pas du tout. count == 0 ne sera que lorsque edittext est vide!
Leo Droidcoder
@MarcAlexander Je ne suis pas sûr de cette réponse, mais vous pouvez vérifier ma solution dans la réponse ci
Leo Droidcoder
5

Ma solution simple qui fonctionne parfaitement. Vous devriez ajouter un drapeau. Mon extrait de code:

editText.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            if (after < count) {
                isBackspaceClicked = true;
            } else {
                isBackspaceClicked = false;
            }
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) { }

        @Override
        public void afterTextChanged(Editable s) {
            if (!isBackspaceClicked) {
                // Your current code
            } else {
                // Your "backspace" handling
            }
        }
Max Zonov
la source
textChangeListner n'a jamais appelé emptTextview.
Janardhan R le
3

Exemple de création d'EditText avec TextWatcher

EditText someEdit=new EditText(this);
//create TextWatcher for our EditText
TextWatcher1 TW1 = new TextWatcher1(someEdit);
//apply our TextWatcher to EditText
        someEdit.addTextChangedListener(TW1);

TextWatcher personnalisé

public class TextWatcher1 implements TextWatcher {
        public EditText editText;
//constructor
        public TextWatcher1(EditText et){
            super();
            editText = et;
//Code for monitoring keystrokes
            editText.setOnKeyListener(new View.OnKeyListener() {
                @Override
                public boolean onKey(View v, int keyCode, KeyEvent event) {
                    if(keyCode == KeyEvent.KEYCODE_DEL){
                        editText.setText("");
                    }
                        return false;
                }
            });
        }
//Some manipulation with text
        public void afterTextChanged(Editable s) {
            if(editText.getText().length() == 12){
                editText.setText(editText.getText().delete(editText.getText().length() - 1, editText.getText().length()));
                editText.setSelection(editText.getText().toString().length());
            }
            if (editText.getText().length()==2||editText.getText().length()==5||editText.getText().length()==8){
                editText.setText(editText.getText()+"/");
                editText.setSelection(editText.getText().toString().length());
            }
        }
        public void beforeTextChanged(CharSequence s, int start, int count, int after){
        }
        public void onTextChanged(CharSequence s, int start, int before, int count) {



        }
    }
Alex Bigalo
la source
1

pour quelqu'un qui utilise Kotlin

addOnTextChanged n'est pas assez flexible pour gérer certains cas (ex: détecter si l'utilisateur appuie sur supprimer lorsque le texte d'édition était vide)

setOnkeyListenertravaillé même clavier souple ou clavier dur! mais juste sur certains appareils . Dans mon cas, cela fonctionne sur Samsung s8 mais pas sur Xiaomi mi8 se.

si vous utilisez kotlin, vous pouvez utiliser la fonction crossline, doOnTextChangedc'est la même chose que addOnTextChangedmais le rappel est déclenché même si le texte d'édition était vide.

REMARQUE: doOnTextChanged fait partie de la bibliothèque Android KTX

Mạnh Hoàng Huynh
la source
2
Vous pourriez probablement spécifier que la doOnTextChanged fonction d'extension est accessible dans la bibliothèque Android KTX
pierre
2
Mais il semble que le rappel ne soit PAS "déclenché même si le texte d'édition était vide". Pourriez-vous s'il vous plaît fournir un extrait de code avec l'interception de suppression (retour arrière) pour vide EditText? Merci d'avance
pierre
1
ah, je l'ai testé lorsque je développe un projet. Dans mon cas, c'est sur xiaomi mi8se, lorsque edittext est vide et que vous appuyez sur Supprimer, aucun rappel n'est déclenché. Je vais chercher un extrait de cette phrase.
Mạnh Hoàng Huynh
0

Cela semble fonctionner pour moi:

public void onTextChanged(CharSequence s, int start, int before, int count) {
    if (before - count == 1) {
        onBackSpace();
    } else if (s.subSequence(start, start + count).toString().equals("\n")) {
        onNewLine();
    }
}
bascule99
la source
0

Je suis également confronté au même problème dans Dialog .. parce que j'utilise setOnKeyListener .. Mais j'ai défini le retour par défaut sur true. Après changement comme ci-dessous, le code fonctionne très bien pour moi.

    mDialog.setOnKeyListener(new Dialog.OnKeyListener() {

        @Override
        public boolean onKey(DialogInterface arg0, int keyCode,
                             KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                mDialog.dismiss();
                return true;
            }
            return false;//this line is important 

        }
    });
Ranjith Kumar
la source
0

Basé sur @Jiff, ZanyEditTextvoici WiseEditTextavecsetSoftKeyListener(OnKeyListener)

package com.locopixel.seagame.ui.custom;

import java.util.Random;

import android.content.Context;
import android.graphics.Color;
import android.support.v7.widget.AppCompatEditText;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;

public class WiseEditText extends AppCompatEditText {

    private Random r = new Random();
    private OnKeyListener keyListener;

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

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

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

    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        return new MyInputConnection(super.onCreateInputConnection(outAttrs),
                true);
    }

    private class MyInputConnection extends InputConnectionWrapper {

        public MyInputConnection(InputConnection target, boolean mutable) {
            super(target, mutable);
        }

        @Override
        public boolean sendKeyEvent(KeyEvent event) {
            if (keyListener != null) {
                keyListener.onKey(WiseEditText.this,event.getKeyCode(),event);
            }
            return super.sendKeyEvent(event);
        }

        @Override
        public boolean deleteSurroundingText(int beforeLength, int afterLength) {       
            // magic: in latest Android, deleteSurroundingText(1, 0) will be called for backspace
            if (beforeLength == 1 && afterLength == 0) {
                // backspace
                return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
                    && sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
            }

            return super.deleteSurroundingText(beforeLength, afterLength);
        }

    }

    public void setSoftKeyListener(OnKeyListener listener){
        keyListener = listener;
    }

}
Qamar
la source
Il est appelé deux fois pour chaque événement de touche de suppression.
Pankaj Kumar
0

Mon problème était que j'avais une coutume Textwatcher, donc je ne voulais pas ajouter OnKeyListenerà un EditTextainsi que je ne voulais pas créer de coutume EditText. Je voulais détecter si le retour arrière était pressé dans ma afterTextChangedméthode, donc je ne devrais pas déclencher mon événement.

C'est ainsi que j'ai résolu ce problème. J'espère que ce serait utile pour quelqu'un.

public class CustomTextWatcher extends AfterTextChangedTextWatcher {

private boolean backspacePressed;

@Override
public void afterTextChanged(Editable s) {
    if (!backspacePressed) {
        triggerYourEvent();
    }
}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
    super.onTextChanged(s, start, before, count);
    backspacePressed = count == 0; //if count == 0, backspace is pressed
}
}
Dmitry Smolyaninov
la source
0

J'ai testé la solution de @ Jeff sur la version 4.2, 4.4, 6.0. Sur les versions 4.2 et 6.0, cela fonctionne bien. Mais sur 4.4, cela ne fonctionne pas.

J'ai trouvé un moyen simple de contourner ce problème. Le point clé est d'insérer un caractère invisible dans le contenu de EditText au début et de ne pas laisser l'utilisateur déplacer le curseur avant ce caractère. Ma façon est d'insérer un caractère d'espace blanc avec un ImageSpan de largeur nulle dessus. Voici mon code.

                @Override
                public void afterTextChanged(Editable s) {
                    String ss = s.toString();
                    if (!ss.startsWith(" ")) {
                        int selection = holder.editText.getSelectionEnd();
                        s.insert(0, " ");
                        ss = s.toString();
                        holder.editText.setSelection(selection + 1);
                    }
                    if (ss.startsWith(" ")) {
                        ImageSpan[] spans = s.getSpans(0, 1, ImageSpan.class);
                        if (spans == null || spans.length == 0) {
                            s.setSpan(new ImageSpan(getResources().getDrawable(R.drawable.zero_wdith_drawable)), 0 , 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                        }
                    }
                }

Et nous avons besoin d'un EditText personnalisé qui a un SelectionChangeListener

public class EditTextSelectable extends android.support.v7.widget.AppCompatEditText {
public interface OnSelectChangeListener {
    void onSelectChange(int start, int end);
}

private OnSelectChangeListener mListener;

public void setListener(OnSelectChangeListener listener) {
    mListener = listener;
}

...constructors...

@Override
protected void onSelectionChanged(int selStart, int selEnd) {
    if (mListener != null) {
        mListener.onSelectChange(selStart, selEnd);
    }
    super.onSelectionChanged(selStart, selEnd);
}

}

Et la dernière étape

holder.editText.setListener(new EditTextSelectable.OnSelectChangeListener() {
                @Override
                public void onSelectChange(int start, int end) {
                    if (start == 0 && holder.editText.getText().length() != 0) {
                        holder.editText.setSelection(1, Math.max(1, end));
                    }
                }
            });

Et maintenant, nous avons terminé ~ Nous pouvons détecter l'événement de touche de retour arrière lorsque EditText n'a pas de contenu réel, et l'utilisateur ne saura rien de notre astuce.

passant
la source
0

Cette question est peut-être ancienne, mais la réponse est vraiment simple en utilisant un TextWatcher.

int lastSize=0;
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    //2. compare the old length of the text with the new one
    //3. if the length is shorter, then backspace was clicked
    if (lastSize > charSequence.length()) {
        //4. Backspace was clicked
        //5. perform action
    }
    //1. get the current length of of the text
    lastSize = charSequence.length();
}
BluRe.CN
la source
Tout comme les solutions précédentes, cela peut être déclenché par la saisie semi-automatique / suggestions.
Stonz2 le
0

J'ai trouvé une solution vraiment simple qui fonctionne avec un clavier logiciel.

override fun onTextChanged(text: CharSequence?, start: Int, before: Int, count: Int) {
    text?.let { 
        if(count < before) {
            Toast.makeText(context, "backspace pressed", Toast.LENGTH_SHORT).show()
            // implement your own code
        }
    }
}
Hawklike
la source
-3

Vous pouvez définir un écouteur de clé sur l'activité et, dans la méthode de rappel, vous pouvez détecter quelle touche l'utilisateur a frappé. Le code ci-dessous est pour votre référence. J'espère que ça aide.

//after user hits keys, this method would be called.
public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (editText.isFocused()) {
            switch (keyCode) {
            case KeyEvent.KEYCODE_DEL:  //delete key
                Log.i("INFO", "delete key hit"); //you should see this log in ddms after you hit delete key
                break;
            }
        }
        return super.onKeyUp(keyCode, event);
    }
Huang
la source
Vérifié cette solution - KEYCODE_DEL sera renvoyé à l'activité uniquement si le texte d'édition ne gère pas cela par lui-même. Par exemple, lorsqu'il n'y a pas de texte dans editText, ou s'il y a du texte, mais que le curseur est au tout début. C'est drôle que dans mon cas, j'ai besoin exactement de ce comportement
Anton Kizema
Dans mon activité, il n'y a pas d'EditText et je fais simplement apparaître le clavier par programmation. J'ai besoin d'attraper chaque touche du clavier logiciel et cela semble la seule solution qui fonctionne. L'autre remplace la méthode dispatchKeyEvent. Malheureusement, à partir de JellyBean, l'IME n'envoie pas de KeyEvent pour la clé DELETE. developer.android.com/reference/android/view/KeyEvent.html
Bemipefe