Zdarzenie klawisza Android EditText delete (backspace)


122

Jak mogę wykryć zdarzenie klawisza delete (backspace) dla editText? Próbowałem użyć TextWatchera, ale kiedy editText jest pusty, kiedy naciskam klawisz Delete, nic się nie dzieje. Chcę wykryć klawisz usuwania, w którym naciśnij przycisk editText, nawet jeśli nie ma tekstu.

Odpowiedzi:


173

UWAGA: onKeyListener nie działa z klawiaturami programowymi.

Można ustawić OnKeyListenerdla Ciebie editText, dzięki czemu można wykryć dowolny klawisz naciśnij
EDIT: Częstym błędem sprawdzamy KeyEvent.KEYCODE_BACKza backspace, ale tak naprawdę to jest KeyEvent.KEYCODE_DEL(Really że nazwa jest bardzo mylące!)

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

9
Właśnie to wypróbowałem, ale onKeyListeners najwyraźniej nie rejestruje backspace.
stefs

3
Nie będzie działać na miękkiej klawiaturze. Działa to tylko w przypadku wejścia sprzętowego.
Varundroid

6
Na moim Nexus4 (bieg Stock KitKat) to robi pracę na klawiaturze oprogramowania.
Matthias

10
Więc jeśli to nie działa w przypadku klawiszy
programowalnych

33
użyj, event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_DELjeśli nie chcesz, aby zdarzenie strzelało dwa razy za jednym naciśnięciem klawisza Backspace
Fonix

83

Minęło trochę czasu, odkąd pytałeś, ale ja miałem ten sam problem. Jak już wspomniał Estel, problem z kluczowymi słuchaczami polega na tym, że działają one tylko z klawiaturami sprzętowymi. Aby to zrobić za pomocą IME (klawiatura miękka) , rozwiązanie jest nieco bardziej skomplikowane.

Jednolity sposób właściwie chcemy nadpisać jest sendKeyEventw w EditText„s InputConnectionklasy. Ta metoda jest wywoływana, gdy w edytorze IME wystąpią kluczowe zdarzenia. Ale aby to przesłonić, musimy zaimplementować niestandardową, EditTextktóra przesłania onCreateInputConnectionmetodę, opakowując domyślny InputConnectionobiekt w klasę proxy! : |

Brzmi skomplikowanie, ale oto najprostszy przykład, jaki mogłem wymyślić:

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

    }

}

Linia z wywołaniem setRandomBackgroundColorjest miejscem, w którym następuje moja specjalna akcja cofania. W takim przypadku zmianaEditText koloru tła.

Jeśli zawyżasz to z XML, pamiętaj, aby użyć pełnej nazwy pakietu jako tagu:

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

27
Niedawno napotkałem ten sam problem na Jelly Bean. Okazało się, że to rozwiązanie w większości działało, z wyjątkiem tego, że musiałem nadpisać deleteSurroundingText (...) zamiast sendKeyEvent (...) (który w ogóle nie był wywoływany). Mam nadzieję, że to pomoże komuś innemu!
Brandon

Ta odpowiedź w połączeniu z komentarzem @Brandon powyżej sprawiła, że ​​zadziałało to dla mnie. Zastanawiam się teraz, jak to będzie działać na urządzeniach sprzed JellyBean.
Christopher Perry,

Działa z zaakceptowaną odpowiedzią na urządzeniach 2.2 i 2.3.
Christoph,

wygląda na to, że dwukrotnie odpalił kluczowy event dla backspace w dniu 2.3 ...: /
Jeff

25
To nie działa, gdy edittext jest pusty, jakieś pomysły, jak uzyskać zdarzenie dla klawisza delete, gdy edittext jest pusty i nie ma tekstu? 4.2
Rickster

69

To tylko dodatek do odpowiedzi Idris, dodający również zastąpienie do deleteSurroundingText. Więcej informacji na ten temat znalazłem tutaj: Android: Backspace in 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);
        }

    }

}

3
Dziękuję Ci! deleteSurroundingTextNieco było dokładnie to, co potrzebne po wypróbowaniu niezliczone inne rozwiązania.
Adam Rosenfield,

5
To rozwiązanie działało bardzo dobrze na poprzednich wersjach Androida, ale niestety deleteSurroundingText jest wywoływane tylko podczas usuwania białych znaków w 4.4 (KitKat). Testowałem zarówno na Nexusie 4, jak i na 7.
Dean

1
wydaje się, że parametr deleteSurroundingText jest wymagany, gdy EditText jest wielowierszowy. Dziwne
Alex Sorokoletov

7
Dzięki za tonę, nie działało bez deleteSurroundText. Android jest tak przypadkowy, że powinni zmienić jego nazwę na androm.
Torsten Ojaperv

2
U mnie to działa, ale nie mogę już usuwać znaków interpunkcyjnych ani spacji!
jaytj95

29

Oto moje proste rozwiązanie, które działa dla wszystkich 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 ...

    } 
}

AKTUALIZACJA 17.04.18 .
Jak wskazano w komentarzach, to rozwiązanie nie śledzi naciśnięcia klawisza Backspace, jeśli EditText jest pusty (tak samo jak większość innych rozwiązań).
Jednak w większości przypadków jest to wystarczające.
PS Gdybym miał dziś stworzyć coś podobnego, zrobiłbym:

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

Następnie użyj go jako zwykłego TextWatchera:

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

Właśnie to czego potrzebuje! Dziękuję Ci!
DH28

9
TextWatcher nie uruchamia się na pustym EditText
Dan Neacșu

@Leo Droidcoder użyłem w podobnym rozwiązaniu. Przejrzyste, zwięzłe i działa idealnie ... na zdrowie.
AJW

ten algorytm ma wadę, jakbyś kliknął spację po wpisaniu, to poprzednia długość jest większa niż s.length
Marcin S.

2
Działa tak długo, jak nie używasz zaznaczenia (autouzupełnianie)
Javatar

13

Wysłałem 2 dni na znalezienie rozwiązania i znalazłem działające :) (na klawiszach programowych)

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

Po dodaniu Texttwatchera do EditText:

yourEditText.addTextChangedListener(textWatcher);

Mam nadzieję, że działa również na innych urządzeniach z Androidem (Samsung, LG itp.).


Urządzenie HTC Desire (HTC jest jednak powszechne :-P)
Junaid

jeśli wpisana jest biała spacja, policz również == 0
Bincy Baby

Genialna odpowiedź nr 1 brachu :)
Gundu Bandgar

6
To kompletnie nie działa. count == 0 będzie tylko wtedy, gdy edittext będzie pusty!
Leo Droidcoder

@MarcAlexander Nie jestem pewien co do tej odpowiedzi, jednak możesz sprawdzić moje rozwiązanie w odpowiedzi powyżej
Leo Droidcoder

5

Moje proste rozwiązanie, które działa doskonale. Powinieneś dodać flagę. Mój fragment kodu:

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

textChangeListner nigdy nie wywołał emptTextview.
Janardhan R

3

Przykład tworzenia EditText za pomocą TextWatchera

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

niestandardowy TextWatcher

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



        }
    }

1

dla kogoś, kto używa Kotlin

addOnTextChanged nie jest wystarczająco elastyczny, aby obsłużyć niektóre przypadki (np. wykryć, czy użytkownik naciśnie klawisz Usuń, gdy tekst edycji był pusty)

setOnkeyListenerdziałał nawet miękka klawiatura lub hardkeyboard! ale tylko na niektórych urządzeniach . W moim przypadku działa na Samsungu s8 ale nie działa na Xiaomi mi8 se.

jeśli używasz kotlin, możesz użyć funkcji crossline, działa doOnTextChangedtak samo, jak addOnTextChangedwywołanie zwrotne, nawet tekst edycji był pusty.

UWAGA: doOnTextChanged jest częścią biblioteki Android KTX


2
Prawdopodobnie możesz określić, że doOnTextChanged funkcja rozszerzenia jest dostępna w bibliotece Android KTX
kamień

2
Ale wygląda na to, że wywołanie zwrotne NIE jest „wyzwalane, nawet tekst edycji był pusty”. Czy możesz podać fragment kodu z przechwyceniem usuwania (cofania) dla pustego EditText? Z góry dziękuję
kamień

1
ach, mam to przetestować, kiedy tworzę projekt. W moim przypadku jest na xiaomi mi8se, gdy edittext jest pusty i naciśniesz klawisz Delete, nie ma wywołania zwrotnego. Poszukam fragmentu tego zdania.
Mạnh Hoàng Huynh


0

Wydaje się, że to działa dla mnie:

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

Mam również do czynienia z tym samym problemem w oknie dialogowym .. ponieważ używam setOnKeyListener .. Ale ustawiam domyślny powrót true. Po zmianie jak w poniższym kodzie to działa dla mnie dobrze ..

    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

Na podstawie @Jiff ZanyEditTexttutaj jest WiseEditTextzsetSoftKeyListener(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;
    }

}

Jest wywoływana dwukrotnie dla każdego zdarzenia klucza usuwania.
Pankaj Kumar

0

Mój problem polegał na tym, że miałem niestandardowy Textwatcher, więc nie chciałem dodawać OnKeyListenerdo niego, EditTextjak również nie chciałem tworzyć niestandardowego EditText. Chciałem wykryć, czy w mojej afterTextChangedmetodzie naciśnięto klawisz Backspace , więc nie powinienem wywoływać zdarzenia.

Oto jak to rozwiązałem. Mam nadzieję, że to byłoby pomocne dla kogoś.

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

Przetestowałem rozwiązanie @ Jeffa w wersji 4.2, 4.4, 6.0. Na 4.2 i 6.0 działa dobrze. Ale na 4.4 to nie działa.

Znalazłem łatwy sposób na obejście tego problemu. Kluczową kwestią jest wstawienie niewidocznego znaku do zawartości EditText na początku i nie pozwól użytkownikowi przesuwać kursora przed tym znakiem. Mój sposób polega na wstawieniu znaku odstępu z elementem ImageSpan o zerowej szerokości. Oto mój kod.

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

Potrzebujemy niestandardowego EditText, który ma 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);
}

}

I ostatni krok

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

A teraz skończyliśmy ~ Możemy wykryć zdarzenie klawisza backspace, gdy EditText nie ma rzeczywistej treści, a użytkownik nie będzie wiedział nic o naszej sztuczce.


0

To pytanie może być stare, ale odpowiedź jest naprawdę prosta przy użyciu TextWatchera.

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

Podobnie jak poprzednie rozwiązania, może to zostać wywołane przez autouzupełnianie / sugestie.
Stonz2

0

Znalazłem naprawdę proste rozwiązanie, które działa z miękką klawiaturą.

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

-3

Możesz ustawić nasłuchiwanie kluczy dla aktywności, a w metodzie wywołania zwrotnego możesz wykryć, który klawisz uderzył użytkownika. Poniższy kod służy do celów informacyjnych. Mam nadzieję, że to pomoże.

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

Sprawdzono to rozwiązanie - KEYCODE_DEL zostanie wyrzucony do działania tylko wtedy, gdy tekst edycji nie poradzi sobie z tym samodzielnie. Na przykład, gdy nie ma tekstu w editText lub jest jakiś tekst, ale kursor znajduje się na samym początku. To zabawne, że w moim przypadku potrzebuję dokładnie takiego zachowania
Anton Kizema

W mojej działalności nie ma EditText i po prostu programowo wyświetlam klawiaturę. Muszę złapać każdy klawisz klawiatury programowej i wydaje się, że to jedyne działające rozwiązanie. Druga przesłania metodę dispatchKeyEvent. Niestety, zaczynając od JellyBean, IME nie wysyła KeyEvent dla klucza DELETE. developer.android.com/reference/android/view/KeyEvent.html
Bemipefe
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.