Evento de tecla Android EditText delete (retroceso)

122

¿Cómo puedo detectar un evento de tecla de eliminación (retroceso) para un editText? Intenté usar TextWatcher, pero cuando editText está vacío, cuando presiono la tecla Eliminar, no pasa nada. Quiero detectar la pulsación de la tecla de eliminación para un editText incluso si no tiene texto.

Buda Gavril
fuente

Respuestas:

173

NOTA: onKeyListenerno funciona con teclados virtuales.

Se puede configurar OnKeyListenerpara usted editTextpara que pueda detectar cualquier pulse la tecla
EDIT: Un error común estamos comprobando KeyEvent.KEYCODE_BACKpara backspace, pero realmente es KeyEvent.KEYCODE_DEL(realmente ese nombre es muy confuso!)

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
fuente
9
Acabo de probarlo, pero aparentemente los onKeyListeners no registran retrocesos.
stefs
3
No funcionará con el teclado virtual. Esto solo funcionará para la entrada de hardware.
Varundroid
6
En mi (Stock con KitKat) Nexus4 esto hace el trabajo para el teclado de software.
Matthias
10
Entonces, si no funciona para las teclas programables, ¿por qué se acepta esta respuesta en / bajo la plataforma Android?
DJphy
33
Úselo event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_DELsi no desea que el evento se dispare dos veces por presionar la tecla de retroceso
Fonix
83

Ha pasado un tiempo desde que preguntaste, pero tuve el mismo problema. Como ya ha mencionado Estel, el problema con los oyentes clave es que solo funcionan con teclados de hardware. Para hacer esto con un IME (teclado virtual) , la solución es un poco más elaborada.

El único método que realmente desea anular es sendKeyEventen el EditText's InputConnectionde clase. Este método se llama cuando ocurren eventos clave en un IME. Pero para anular esto, necesitamos implementar una costumbre EditTextque anule el onCreateInputConnectionmétodo, ajustando el valor predeterminadoInputConnection objeto en una clase de proxy! : |

Suena complicado, pero aquí está el ejemplo más simple que pude idear:

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 línea con la llamada a setRandomBackgroundColores donde ocurre mi acción especial de retroceso. En este caso, cambiando elEditText color de fondo de.

Si está inflando esto desde XML, recuerde usar el nombre completo del paquete como etiqueta:

<cc.buttfu.test.ZanyEditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/somefield"
></cc.buttfu.test.ZanyEditText>
Idris
fuente
27
Recientemente me encontré con el mismo problema en Jelly Bean. Descubrí que esta solución funcionaba principalmente, excepto que tuve que anular deleteSurroundingText (...) en lugar de sendKeyEvent (...) (que no se estaba llamando en absoluto). ¡Espero que esto ayude a alguien más!
Brandon
Esta respuesta, combinada con el comentario de @Brandon anterior, hizo que esto funcionara para mí. Lo que me pregunto ahora es cómo funcionará esto en dispositivos anteriores a JellyBean.
Christopher Perry
Funciona con la respuesta aceptada en dispositivos 2.2 y 2.3 para mí.
Christoph
parece que está disparando el evento clave para retroceder dos veces en 2.3 ...: /
Jeff
25
Esto no funciona cuando el texto de edición está vacío, ¿alguna idea sobre cómo obtener un evento para la tecla de eliminación cuando el texto de edición está vacío y no tiene texto? 4.2
Rickster
69

Esto es solo una adición a la respuesta de Idris, agregando la anulación para deleteSurroundingText también. Encontré más información sobre eso aquí: Android: Retroceso en 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
fuente
3
¡Gracias! El deleteSurroundingTextbit era exactamente lo que necesitaba después de probar muchas otras soluciones.
Adam Rosenfield
5
Esta solución me ha funcionado muy bien en versiones anteriores de Android, pero desafortunadamente solo se llama a deleteSurroundingText cuando se eliminan los espacios en blanco en 4.4 (KitKat). He probado tanto en Nexus4 como en 7.
Dean
1
parece que se requiere deleteSurroundingText cuando EditText es multilínea. Extraño
Alex Sorokoletov
7
Muchas gracias, no funcionó sin deleteSurroundText. Android es tan aleatorio que deberían cambiarle el nombre a androm.
Torsten Ojaperv
2
Me funciona, ¡pero ya no puedo eliminar la puntuación ni los espacios!
jaytj95
29

Aquí está mi solución fácil, que funciona para todas las 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 ...

    } 
}

ACTUALIZACIÓN 17.04.18 .
Como se señaló en los comentarios, esta solución no rastrea la presión de retroceso si EditText está vacío (lo mismo que la mayoría de las otras soluciones).
Sin embargo, es suficiente para la mayoría de los casos de uso.
PD: Si tuviera que crear algo similar hoy, haría:

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

Luego utilícelo como un 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
fuente
¡Justo lo que necesito! ¡Gracias!
DH28
9
El TextWatcher no se activa en un EditText vacío
Dan Neacșu
@Leo Droidcoder que utilicé en una solución similar. Claro, conciso y funciona a la perfección ... salud.
AJW
este algoritmo tiene una falla como si hace clic en el espacio después de escribir, entonces la longitud anterior es mayor que la longitud
Marcin S.
2
Funciona siempre que no use la selección (autocompletado)
Javatar
13

Envié 2 días para encontrar una solución y descubrí una que funciona :) (en teclas de función)

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

Después de agregar el textwatcher a su EditText:

yourEditText.addTextChangedListener(textWatcher);

Espero que también funcione en otros dispositivos Android (Samsung, LG, etc.).

JP
fuente
Dispositivo HTC desire (HTC es común, aunque :-P)
Junaid
si se escribe uno es un espacio en blanco, entonces también cuenta == 0
Bincy Baby
Brilliant no 1 respuesta hermano :)
Gundu Bandgar
6
Eso no funciona completamente. count == 0 será solo cuando edittext esté vacío!
Leo Droidcoder
@MarcAlexander No estoy seguro de esta respuesta, sin embargo, puede verificar mi solución en la respuesta anterior
Leo Droidcoder
5

Mi solución simple que funciona a la perfección. Deberías agregar una bandera. Mi fragmento de código:

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
fuente
textChangeListner nunca llamó a emptTextview.
Janardhan R
3

Ejemplo de creación de EditText con 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 personalizado

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
fuente
1

para alguien que usa Kotlin

addOnTextChanged no es lo suficientemente flexible para manejar algunos casos (por ejemplo: detectar si el usuario presiona eliminar cuando el texto de edición estaba vacío)

setOnkeyListener¡Funcionó incluso con el teclado suave o el teclado físico! pero solo en algunos dispositivos . En mi caso, funciona en Samsung s8 pero no funciona en Xiaomi mi8 se.

si usa kotlin, puede usar la función crossline doOnTextChanged, es lo mismo que addOnTextChangedpero la devolución de llamada se activa incluso el texto de edición estaba vacío.

NOTA: doOnTextChanged es parte de la biblioteca KTX de Android

Mạnh Hoàng Huynh
fuente
2
Probablemente pueda especificar que doOnTextChanged se puede acceder a la función de extensión en la biblioteca KTX de Android
piedra
2
Pero parece que la devolución de llamada NO "se activa, incluso la edición de texto estaba vacía". ¿Podría proporcionar algún fragmento con la intercepción de eliminación (retroceso) para el vacío EditText? Gracias de antemano
piedra
1
ah, lo he probado cuando desarrollo un proyecto. En mi caso, en xiaomi mi8se, cuando edittext está vacío y presionas eliminar, no se activa la devolución de llamada. Buscaré un fragmento de esta oración.
Mạnh Hoàng Huynh
0

Esto parece funcionar para mí:

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

También me enfrento al mismo problema en Dialog ... porque estoy usando setOnKeyListener ... Pero establezco el retorno predeterminado verdadero. Después de cambiar como el código siguiente, funciona bien para mí.

    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
fuente
0

Basado en @Jiff ZanyEditTextaquí está WiseEditTextconsetSoftKeyListener(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
fuente
Se llama dos veces por cada evento de eliminación de clave.
Pankaj Kumar
0

Mi problema era que tenía personalizado Textwatcher, por lo que no quería agregar OnKeyListenera un EditTextpozo ni tampoco quería crear personalizado EditText. Quería detectar si se presionó la tecla de retroceso en miafterTextChanged método, por lo que no debería activar mi evento.

Así es como resolví esto. Espero que sea de ayuda para alguien.

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
fuente
0

He probado la solución de @ Jeff en la versión 4.2, 4.4, 6.0. En 4.2 y 6.0, funciona bien. Pero en 4.4, no funciona.

Encontré una manera fácil de solucionar este problema. El punto clave es insertar un carácter invisible en el contenido de EditText al principio, y no permitir que el usuario mueva el cursor antes de este carácter. Mi forma es insertar un carácter de espacio en blanco con un ImageSpan de ancho cero en él. Aquí está mi código.

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

Y necesitamos un EditText personalizado que tenga 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);
}

}

Y el ultimo paso

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

Y ahora, hemos terminado ~ Podemos detectar el evento de tecla de retroceso cuando EditText no tiene contenido real, y el usuario no sabrá nada sobre nuestro truco.

transeúnte
fuente
0

Esta pregunta puede ser antigua, pero la respuesta es realmente simple usando 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
fuente
Al igual que las soluciones anteriores, esto se puede activar mediante autocompletar / sugerencias.
Stonz2
0

Encontré una solución realmente simple que funciona con un teclado virtual.

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
        }
    }
}
Como un halcón
fuente
-3

Puede establecer un oyente clave en la actividad y, en el método de devolución de llamada, puede detectar qué tecla presionó el usuario. El siguiente código es para su referencia. Espero eso ayude.

//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
fuente
Verificó esta solución: KEYCODE_DEL se lanzará a la actividad solo si la edición de texto no se encarga de esto por sí sola. Por ejemplo, cuando no hay texto en editText, o hay algo de texto, pero el cursor está al principio. Es gracioso que en mi caso necesite exactamente ese comportamiento
Anton Kizema
En mi actividad no hay EditText y solo hago que el teclado aparezca programáticamente. Necesito captar cada tecla del teclado virtual y esta parece la única solución que funciona. El otro anula el método dispatchKeyEvent. Desafortunadamente, a partir de JellyBean, el IME no envía un KeyEvent para la tecla DELETE. developer.android.com/reference/android/view/KeyEvent.html
Bemipefe