Android EditText удалить (backspace) ключевое событие

123

Как я могу обнаружить ключевое событие delete (backspace) для editText? Я пробовал использовать TextWatcher, но когда editText пуст, когда я нажимаю клавишу удаления, ничего не происходит. Я хочу обнаружить нажатие клавиши удаления для editText, даже если у него нет текста.

Буда Гаврил
источник

Ответы:

174

ПРИМЕЧАНИЕ: onKeyListenerне работает для программных клавиатур.

Вы можете установить OnKeyListenerдля вас , editTextтак что вы можете обнаружить любую клавишу нажмите
EDIT: Обычную ошибку мы проверка KeyEvent.KEYCODE_BACKдля backspace, но на самом деле это KeyEvent.KEYCODE_DEL(Действительно , что имя очень запутанные!)

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
Я просто попробовал, но onKeyListeners, по-видимому, не регистрирует обратные пространства.
Stefs
3
Для мягкой клавиатуры это не сработает. Это будет работать только для аппаратного ввода.
Varundroid 05
6
На мой Nexus4 (работает на складе KitKat) , это делает работу для программной клавиатуры.
Матиас
10
Итак, если он не работает для программных клавиш, то почему этот ответ принят на платформе Android или под ней ..
DJphy
33
используйте, event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_DELесли вы не хотите, чтобы событие запускалось дважды при нажатии клавиши Backspace
Fonix
83

Прошло некоторое время с тех пор, как вы спросили, но у меня была такая же проблема. Как уже упоминала Эстель, проблема с ключевыми слушателями заключается в том, что они работают только с аппаратными клавиатурами. Чтобы сделать это с помощью IME (программной клавиатуры) , решение немного сложнее.

Единственный метод , который мы действительно хотим переопределить это sendKeyEventв EditText«S InputConnectionкласса. Этот метод вызывается при возникновении ключевых событий в IME. Но для того, чтобы переопределить это, нам нужно реализовать кастом, EditTextкоторый переопределяет onCreateInputConnectionметод, заключая InputConnectionобъект по умолчанию в прокси-класс! : |

Звучит сложно, но вот простейший пример, который я смог придумать:

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

    }

}

Строка с призывом setRandomBackgroundColor- это то место, где происходит мое специальное действие возврата. В этом случае изменениеEditText цвета фона.

Если вы раздуваете это из XML, не забудьте использовать полное имя пакета в качестве тега:

<cc.buttfu.test.ZanyEditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/somefield"
></cc.buttfu.test.ZanyEditText>
Идрис
источник
27
Недавно я столкнулся с той же проблемой на Jelly Bean. Я обнаружил, что это решение в основном работает, за исключением того, что мне пришлось переопределить deleteSurroundingText (...) вместо sendKeyEvent (...) (который вообще не вызывался). Надеюсь, это поможет кому-то другому!
Brandon
Этот ответ в сочетании с комментарием @Brandon выше помог мне. Теперь мне интересно, как это будет работать на устройствах, предшествующих JellyBean.
Кристофер Перри
Он работает с принятым ответом на устройствах 2.2 и 2.3 для меня.
Кристоф
похоже, что он запускает ключевое событие для backspace дважды в 2.3 ...: /
Джефф
25
Это не работает, когда текст редактирования пуст, есть идеи о том, как получить событие для клавиши удаления, когда текст редактирования пуст и не имеет текста? 4.2
Rickster
69

Это просто дополнение к ответу Идриса, а также добавление переопределения для deleteSurroundingText. Я нашел больше информации об этом здесь: Android: Backspace в 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
Спасибо! deleteSurroundingTextНемного было именно то , что мне нужно было после попытки множества других решений.
Адам Розенфилд
5
Это решение очень хорошо работало для меня в предыдущих версиях Android, но, к сожалению, deleteSurroundingText вызывается только при удалении пробелов в 4.4 (KitKat). Я тестировал как Nexus4, так и 7.
Дин
1
кажется, что deleteSurroundingText требуется, когда EditText является многострочным. Weird
Алекс Сороколетов
7
Большое спасибо, не работало без deleteSurroundText. Android настолько случайен, что они должны переименовать его в androm.
Торстен Ояперв
2
У меня это работает, но я больше не могу удалять знаки препинания и пробелы!
jaytj95
29

Вот мое простое решение, которое работает для всех 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 ...

    } 
}

ОБНОВЛЕНИЕ 17.04.18 .
Как указано в комментариях, это решение не отслеживает нажатие клавиши Backspace, если EditText пуст (как и большинство других решений).
Однако этого достаточно для большинства случаев использования.
PS Если бы мне сегодня пришлось создать что-то подобное, я бы сделал:

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

Затем просто используйте его как обычный TextWatcher:

 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 
        }
    });
Лев Droidcoder
источник
Как раз то, что мне нужно! Спасибо!
DH28,
9
TextWatcher не срабатывает при пустом EditText
Дэн Неакку
@Leo Droidcoder я использовал в аналогичном решении. Ясно, кратко и отлично работает ... ура.
AJW
у этого алгоритма есть недостаток, так как если вы нажимаете пробел после ввода, тогда предыдущая длина больше s.length
Марчин С.
2
Работает, пока вы не используете выделение (автозаполнение)
Javatar
13

Отправил 2 дня на поиск решения и разобрался рабочий :) (по софт клавишам)

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

После добавления текстового наблюдателя в ваш EditText:

yourEditText.addTextChangedListener(textWatcher);

Надеюсь, он работает и на других устройствах Android (samsung, LG и т. Д.).

JP
источник
Устройство HTC desire (хотя и распространено HTC :-P)
Junaid
если введено одно - пробел, то также count == 0
Bincy Baby
Блестящий ответ № 1, братан :)
Гунду Бандгар
6
Это совершенно не работает. count == 0 будет только тогда, когда edittext пуст!
Leo Droidcoder
@MarcAlexander Я не уверен в этом ответе, однако вы можете проверить мое решение в ответе выше
Leo Droidcoder
6

Мое простое решение, которое отлично работает. Вы должны добавить флаг. Мой фрагмент кода:

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 никогда не вызывал emptTextview.
Джанардхан Р.
3

Пример создания EditText с 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

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

для тех, кто использует Котлин

addOnTextChanged недостаточно гибок, чтобы обрабатывать некоторые случаи (например: определить, нажимает ли пользователь удаление, когда текст редактирования был пуст)

setOnkeyListenerработала даже мягкая клавиатура или жесткая клавиатура! но только на некоторых устройствах . В моем случае он работает на Samsung s8, но не работает на Xiaomi mi8 se.

если вы используете kotlin, вы можете использовать функцию кросслайна, doOnTextChangedэто то же самое, addOnTextChangedно обратный вызов запускается, даже если текст редактирования был пуст.

ПРИМЕЧАНИЕ: doOnTextChanged является частью библиотеки Android KTX.

Мун Хоанг Хюинь
источник
2
Вы могли бы , вероятно , указать , что doOnTextChanged функция расширения доступна в Android KTX библиотеки
камень
2
Но похоже, что обратный вызов НЕ «запускается, даже если текст редактирования был пуст». Не могли бы вы предоставить фрагмент с перехватом удаления (backspace) для пустого EditText? Спасибо заранее
камень
1
ах, я тестирую это, когда разрабатываю проект. В моем случае на xiaomi mi8se, когда edittext пуст и вы нажимаете delete, обратный вызов не запускается. Я найду отрывок для этого предложения.
Mạnh Hoàng Huynh
0

В Stackoverflow есть аналогичный вопрос. Вам нужно переопределить EditText, чтобы получить доступ к InputConnectionобъекту, который содержит deleteSurroundingTextметод. Это поможет вам обнаружить событие удаления (возврата). Пожалуйста, взгляните на решение, которое я предоставил там Android - не может захватывать backspace / delete press в soft. клавиатура

Аяз Алифов
источник
0

Кажется, это работает для меня:

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
источник
0

Я также столкнулся с такой же проблемой в Dialog .. потому что я использую setOnKeyListener .. Но я установил значение return true. После изменения, как показано ниже, у меня все работает нормально.

    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

На основе @Jiff ZanyEditTextздесь WiseEditTextс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

Моя проблема была, что у меня был обычай Textwatcher, так что я не хочу , чтобы добавить OnKeyListenerк EditTextтак же как я не хотел создавать собственные EditText. Я хотел определить, была ли нажата клавиша Backspace в моем afterTextChangedметоде, поэтому я не должен запускать свое событие.

Вот как я это решил. Надеюсь, это будет кому-то полезно.

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

Я тестировал решение @ Jeff на версиях 4.2, 4.4, 6.0. На 4.2 и 6.0 работает хорошо. Но на 4.4 не работает.

Я нашел простой способ обойти эту проблему. Ключевым моментом является вставка невидимого символа в содержимое EditText в самом начале и не позволять пользователю перемещать курсор перед этим символом. Мой способ - вставить символ пробела с ImageSpan нулевой ширины. Вот мой код.

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

И нам нужен пользовательский EditText, у которого есть 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);
}

}

И последний шаг

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

И теперь мы закончили. Мы можем обнаружить событие клавиши Backspace, когда EditText не имеет фактического содержимого, и пользователь ничего не знает о нашем трюке.

passerbywhu
источник
0

Этот вопрос может быть старым, но ответ очень прост с использованием 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
источник
Как и в предыдущих решениях, это может быть вызвано автозаполнением / предложениями.
Stonz2
0

Я нашел действительно простое решение, которое работает с мягкой клавиатурой.

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

Вы можете установить ключевой прослушиватель для действия, а в методе обратного вызова вы можете определить, какой ключ нажимает пользователь. Код ниже предназначен для справки. Надеюсь, поможет.

//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
источник
Проверил это решение - KEYCODE_DEL будет активирован, только если текст редактирования не справится с этим сам по себе. Например, когда в editText нет текста или есть текст, но курсор находится в самом начале. Забавно, что в моем случае мне нужно именно такое поведение,
Антон Кзема,
В моей деятельности нет EditText, и я просто программно отображаю клавиатуру. Мне нужно поймать каждую программную клавишу клавиатуры, и это кажется единственным рабочим решением. Другой переопределяет метод dispatchKeyEvent. К сожалению, начиная с JellyBean, IME не отправляет KeyEvent для клавиши DELETE. developer.android.com/reference/android/view/KeyEvent.html
Bemipefe,