Evento-chave de exclusão do Android EditText (backspace)

122

Como posso detectar um evento de tecla delete (backspace) para um editText? Eu tentei usar o TextWatcher, mas quando o editText está vazio, quando pressiono a tecla Delete, nada acontece. Eu quero detectar a tecla delete e pressionar um editText, mesmo que não tenha texto.

Buda Gavril
fonte

Respostas:

172

NOTA: onKeyListenernão funciona para teclados flexíveis.

Você pode definir OnKeyListenerpara você editTextpara que você possa detectar qualquer pressione a tecla
EDIT: Um erro comum estamos verificando KeyEvent.KEYCODE_BACKpara backspace, mas realmente é KeyEvent.KEYCODE_DEL(realmente esse nome é muito 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
fonte
9
Eu apenas tentei, mas onKeyListeners aparentemente não registram backspaces.
stefs 30/06/11
3
Não funcionará para teclado virtual. Isso funcionará apenas para entrada de hardware.
Varundroid
6
No meu (estoque correndo KitKat) Nexus4 este faz o trabalho para o teclado software.
Matthias
10
Então, se ele não funciona para teclas de função, então porque é que esta resposta aceita in / sob a plataforma android ..
DJphy
32
usar event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_DELse você não quiser evento ao fogo duas vezes por pressionar backspace
Fonix
83

Já faz um tempo desde que você perguntou, mas eu só tive o mesmo problema. Como já mencionado por Estel, o problema com os principais ouvintes é que eles só funcionam com teclados de hardware. Para fazer isso com um IME (teclado virtual) , a solução é um pouco mais elaborada.

O método único que realmente queremos substituir está sendKeyEventna classe EditText's InputConnection. Este método é chamado quando os principais eventos ocorrem em um IME. Mas, para substituir isso, precisamos implementar um costume EditTextque substitua o onCreateInputConnectionmétodo, envolvendo o InputConnectionobjeto padrão em uma classe proxy! : |

Parece complicado, mas aqui está o exemplo mais simples que eu poderia inventar:

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

    }

}

A linha com a chamada para setRandomBackgroundColoré onde minha ação especial de backspace ocorre. Nesse caso, alterando a EditTextcor de fundo do.

Se você estiver inflando isso a partir do XML, lembre-se de usar o nome completo do pacote como a tag:

<cc.buttfu.test.ZanyEditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/somefield"
></cc.buttfu.test.ZanyEditText>
Idris
fonte
27
Recentemente, encontrei o mesmo problema no Jelly Bean. Eu achei que essa solução funcionava principalmente, exceto que eu tinha que substituir deleteSurroundingText (...) em vez de sendKeyEvent (...) (que não estava sendo chamado). Espero que isso ajude alguém!
Brandon
Esta resposta, combinada com o comentário do @Brandon acima, fez isso funcionar para mim. O que eu estou querendo saber agora é como isso funcionará em dispositivos pré-JellyBean.
22413 Christopher Christopher
Ele funciona com a resposta aceita em dispositivos 2.2 e 2.3 para mim.
11403 Christoph
parece que está disparando o evento principal do backspace duas vezes em 2.3 ...: /
Jeff
25
Isso não funciona quando o edittext está vazio, alguma idéia de como obter um evento para a chave de exclusão quando o edittext está vazio e não possui texto? 4.2
Rickster
69

Esta é apenas uma adição à resposta de Idris, adicionando a substituição para deleteSurroundingText também. Encontrei mais informações sobre isso aqui: Android: Backspace no 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
fonte
3
Obrigado! A parte deleteSurroundingTextera exatamente o que eu precisava depois de tentar inúmeras outras soluções.
Adam Rosenfield
5
Esta solução funcionou muito bem para mim nas versões anteriores do Android, mas infelizmente deleteSurroundingText é chamado apenas ao remover o espaço em branco no 4.4 (KitKat). Eu testei no Nexus4 e no 7. #
Dean
1
parece que deleteSurroundingText é necessário quando EditText é multilinha. Estranho
Alex Sorokoletov
7
Obrigado, cara, não funcionou com deleteSurroundText. O Android é tão aleatório que eles devem renomeá-lo para androm.
Torsten Ojaperv
2
Funciona para mim, mas não consigo mais excluir pontuação ou espaços!
jaytj95
29

Aqui está minha solução fácil, que funciona para todas as APIs:

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

    } 
}

ATUALIZAÇÃO 17.04.18 .
Conforme indicado nos comentários, esta solução não rastreia a pressão de backspace se o EditText estiver vazio (o mesmo que a maioria das outras soluções).
No entanto, é suficiente para a maioria dos casos de uso.
PS Se eu tivesse que criar algo semelhante hoje, faria:

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

Em seguida, basta usá-lo como um TextWatcher comum:

 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
fonte
Exatamente o que eu preciso! Obrigado!
DH28 19/09/16
9
Faz o TextWatcher não desencadear em um EditText vazio
Dan Neacşu
@ Leo Droidcoder eu usei em uma solução semelhante. Clara, concisa e funciona perfeitamente ... felicidades.
AJW
este algoritmo tem uma falha como se você clicar em espaço depois de digitar, em seguida, o comprimento anterior é maior do que s.length
Marcin S.
2
Funciona, desde que você não use a seleção (autocompletar)
Javatar
13

Enviei 2 dias para encontrar uma solução e descobri uma que funcionava :) (em teclas de função)

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

Depois de adicionar o observador de texto ao seu EditText:

yourEditText.addTextChangedListener(textWatcher);

Espero que ele funcione em outros dispositivos Android também (Samsung, LG, etc).

JP
fonte
HTC dispositivo desejo (HTC é comum que -P)
Junaid
se alguém digitou é espaço em branco, em seguida, também contam == 0
Bincy Bebê
Brilhante no 1 resposta bro :)
Gundu Bandgar
6
Isso não funciona completamente. count == 0 será apenas quando o edittext estiver vazio!
DROIDCODO Leo
@MarcAlexander Não estou certo sobre esta resposta, contudo, você pode verificar a minha solução na resposta acima
Leo Droidcoder
5

Minha solução simples que funciona perfeitamente. Você deve adicionar uma bandeira. Meu trecho 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
fonte
O textChangeListner nunca chamou o emptTextview.
Janardhan R
3

Exemplo de criação de EditText com 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
fonte
1

para alguém que está usando Kotlin

addOnTextChanged não é flexível o suficiente para lidar com alguns casos (por exemplo: detectar se o usuário pressiona excluir quando o texto de edição estava vazio)

setOnkeyListenertrabalhou mesmo teclado virtual ou teclado! mas apenas em alguns dispositivos . No meu caso, ele funciona no Samsung s8, mas não no Xiaomi mi8 se.

se você estiver usando o kotlin, poderá usar a função de linha cruzada doOnTextChanged, é a mesma coisa, addOnTextChangedmas o retorno de chamada é acionado até a edição de texto estava vazia.

NOTA: doOnTextChanged faz parte da biblioteca Android KTX

Mạnh Hoàng Huynh
fonte
2
Você provavelmente pode especificar que a doOnTextChanged função de extensão está acessível na biblioteca Android KTX
stone
2
Mas parece que o retorno de chamada NÃO é "acionado, mesmo o texto de edição estava vazio". Você poderia fornecer algum snippet com a interceptação delete (backspace) para vazio EditText? Agradecemos antecipadamente
stone
1
ah, eu testei quando desenvolvo um projeto. No meu caso, está no xiaomi mi8se, quando o edittext está vazio e você pressiona excluir, nenhum retorno de chamada é acionado. Vou procurar um trecho para esta frase.
M Honh Hoàng Huynh
0

Isso parece estar funcionando para mim:

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

Também estou enfrentando o mesmo problema no Dialog .. porque estou usando setOnKeyListener .. Mas defino o retorno padrão true. Após a alteração como no código abaixo, está funcionando bem para mim ..

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

Baseado em @Jiff ZanyEditTextaqui é WiseEditTextcomsetSoftKeyListener(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
fonte
Ele está sendo chamado duas vezes para cada evento da chave de exclusão.
Pankaj Kumar
0

Meu problema era que eu tinha personalizado Textwatcher, então não queria adicionar OnKeyListenera um EditTexte não queria criar personalizado EditText. Eu queria detectar se o backspace foi pressionado no meuafterTextChanged método, para não acionar meu evento.

Foi assim que resolvi isso. Espero que seja útil para alguém.

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

Testei a solução de Jeff na versão 4.2, 4.4, 6.0. Nos 4.2 e 6.0, funciona bem. Mas no 4.4, não funciona.

Encontrei uma maneira fácil de solucionar esse problema. O ponto principal é inserir um caractere invisível no conteúdo do EditText no início e não permitir que o usuário mova o cursor antes desse caractere. Minha maneira é inserir um caractere de espaço em branco com um ImageSpan de largura zero. Aqui está o meu 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);
                        }
                    }
                }

E precisamos customizar um EditText que tenha um 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);
}

}

E o ultimo passo

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

E agora estamos prontos ~ Podemos detectar o evento chave de backspace quando o EditText não possui conteúdo real e o usuário não sabe nada sobre o nosso truque.

passerbywhu
fonte
0

Esta pergunta pode ser antiga, mas a resposta é realmente simples usando um 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
fonte
Assim como as soluções anteriores, isso pode ser acionado pelo preenchimento automático / sugestões.
Stonz2 17/01
0

Eu encontrei uma solução realmente simples que funciona com um 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
        }
    }
}
Hawklike
fonte
-3

Você pode definir um ouvinte de chave na atividade e, no método de retorno de chamada, pode detectar qual chave o usuário pressionou. O código abaixo é para sua referência. Espero que ajude.

//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
fonte
Verificada esta solução - KEYCODE_DEL será lançada em atividade somente se o texto de edição não resolver isso sozinho. Por exemplo, quando não há texto no editText, ou existe algum texto, mas o cursor está no início. Isso é engraçado que no meu caso eu preciso exatamente o que o comportamento
Anton Kizema
Na minha atividade, não há EditText e apenas faço o teclado aparecer programaticamente. Preciso pegar todas as teclas do teclado e essa parece ser a única solução. O outro está substituindo o método dispatchKeyEvent. Infelizmente, a partir do JellyBean, o IME não envia um KeyEvent para a chave DELETE. developer.android.com/reference/android/view/KeyEvent.html
Bemipefe