web-dev-qa-db-fra.com

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

Comment détecter un événement de clé delete (backspace) pour un editText? J'ai essayé d'utiliser TextWatcher, mais quand editText est vide, lorsque j'appuie sur la touche Suppr, rien ne se produit. Je veux détecter une touche de suppression, appuyez sur un texte d'édition même s'il ne contient pas de texte. 

98
Buda Gavril

REMARQUE: onKeyListener ne fonctionne pas pour les claviers logiciels.

Vous pouvez définir OnKeyListener pour vous editText afin de détecter toute pression sur une touche.
EDIT: Une erreur commune: nous vérifions KeyEvent.KEYCODE_BACK pour backspace, mais c’est vraiment KeyEvent.KEYCODE_DEL (ce nom est vraiment 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;       
    }
});
127
Labeeb Panampullan

Cela fait un moment que vous avez demandé, mais je viens d'avoir le même problème. Comme mentionné par Estel, le problème des écouteurs clés est qu'ils ne fonctionnent qu'avec des claviers matériels. Pour ce faire avec un IME (clavier logiciel) , la solution est un peu plus élaborée. 

La seule méthode que nous voulons réellement remplacer est sendKeyEvent dans la classe EditText's InputConnection. Cette méthode est appelée lorsque des événements clés se produisent dans un IME. Mais pour redéfinir cela, nous devons implémenter un EditText personnalisé, qui redéfinit la méthode onCreateInputConnection, encapsulant l'objet InputConnection par défaut dans une classe proxy! : |

Cela semble compliqué, mais voici l'exemple le plus simple que j'ai pu inventer:

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ù se produit mon action spéciale de retour arrière. Dans ce cas, changez la couleur d'arrière-plan de la EditText.

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

<cc.buttfu.test.ZanyEditText
Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
Android:id="@+id/somefield"
></cc.buttfu.test.ZanyEditText>
78
Idris

Ceci est juste un ajout à la réponse d'Idris, ajoutant également le remplacement à deleteSurroundingText. J'ai trouvé plus d'informations à ce sujet ici: Android: Retour arrière 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);
        }

    }

}
60
Jeff

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 détecte pas la pression de retour arrière si EditText est vide (comme dans la plupart des autres solutions).
Cependant, cela suffit pour la plupart des cas d'utilisation.
P.S. 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 ordinaire:

 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 
        }
    });
22
Leo Droidcoder

J'ai envoyé 2 jours pour trouver une solution et j'en ai trouvé une qui fonctionnait :)

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 aussi sur d'autres appareils Android (Samsung, LG, etc.).

11
J.P

Exemple de création 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) {



        }
    }
3
Alex Bigalo

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

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

J'ai trouvé un moyen facile 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. Mon moyen est d'insérer un caractère d'espace blanc avec un ImageSpan de largeur nulle. 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é avec 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 un événement de touche de retour arrière quand EditText n'a pas de contenu réel, et l'utilisateur ne saura rien de notre astuce.

0
passerbywhu

pour quelqu'un qui utilise Kotlin

addOnTextChangedn'est pas assez flexible pour traiter certains cas (par exemple, détecter si l'utilisateur appuie sur Supprimer lorsque le texte modifié était vide)

setOnkeyListener a même travaillé clavier, clavier ou clavier dur! mais seulement 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 de reliure doOnTextChanged, identique à addOnTextChanged mais le rappel est déclenché même si le texte de montage était vide.

0
Mạnh Hoàng Huynh

Je suis également confronté au même problème dans Dialog .. parce que j'utilise setOnKeyListener .. Mais j'ai défini default return true. Après le changement, comme ci-dessous, le code fonctionne 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 

        }
    });
0
Ranjith Kumar

Basé sur @Jiff ZanyEditText, voici WiseEditText avec setSoftKeyListener(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;
    }

}
0
Qamar

Cette question est peut-être ancienne, mais la réponse est très simple avec 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();
}
0
BluRe.CN

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

Il y a une question similaire dans le Stackoverflow. Vous devez remplacer EditText pour avoir accès à l'objet InputConnection qui contient la méthode deleteSurroundingText. Cela vous aidera à détecter un événement de suppression (retour arrière). S'il vous plaît, jetez un oeil à une solution que j'ai fourni là-bas Android - ne peut pas capturer de retour arrière/supprimer la presse au soft clavier

0
Sa Qada

Mon problème était que j'avais Textwatcher personnalisé, alors je ne voulais pas ajouter OnKeyListener à EditText et je ne voulais pas créer de EditText personnalisé. Je voulais détecter si le retour arrière a été activé dans ma méthode afterTextChanged. Je ne devrais donc pas déclencher mon événement.

C'est comme ça que j'ai résolu ça. 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
}
}
0

J'ai trouvé une solution très 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
        }
    }
}
0
Hawklike