Ключова подія видалення (повернення) Android EditText


122

Як я можу виявити ключову подію видалення (повернення назад) для editText? Я намагався використовувати TextWatcher, але коли правка editText порожня, коли я натискаю клавішу видалення, нічого не відбувається. Я хочу виявити клавішу видалення, натисніть foe editText, навіть якщо в ній немає тексту.

Відповіді:


172

ПРИМІТКА: 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
Це не працюватиме для м’якої клавіатури. Це буде працювати лише для апаратного введення.
Варундоїд

6
На мій Nexus4 (працює на складі KitKat) , це робить роботу для програмної клавіатури.
Маттіас

10
Так, якщо це не працює для програмних клавіш, то чому ця відповідь прийнята в / під андроїд платформою ..
DJphy

32
використовуйте, event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_DELякщо ви не хочете, щоб випал двічі за натиск зворотної області
Fonix

83

Минув час, коли ви запитали, але у мене просто було те саме. Як вже зазначав Estel, проблема з ключовими слухачами полягає в тому, що вони працюють лише з апаратними клавіатурами. Для цього за допомогою 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, описаним вище, зробила цю роботу мені Мені зараз цікаво, як це працюватиме на пристроях, що передують JellyBean.
Крістофер Перрі

Це працює з прийнятою відповіддю на пристроях 2.2 та 2.3.
Крістоф

здається, що це двічі запускає ключову подію для зворотного простору 2.3 ...: /
Джефф

25
Це не працює, коли редакційний текст порожній, якісь ідеї, як отримати подію для ключа видалення, коли редакційний текст порожній і не має тексту? 4.2
Рікстер

69

Це лише доповнення до відповіді Ідріса, що також додає переосмислення для видаленняSurroundingText. Більше інформації про це я знайшов тут: 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 є багаторядковим. Дивно
Олексій Сороколетов

7
Дякую тонну людину, не вийшло з deleteSurroundText. Android настільки випадковий, що їм слід перейменувати його на androm.
Torsten Ojaperv

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 .
Як зазначалося в коментарях, це рішення не відстежує натискання зворотної області, якщо 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 
        }
    });

Тільки те, що мені потрібно! Дякую!
DH28

9
TextWatcher не спрацьовує на порожньому EditText
Dan Neacșu

@Leo Droidcoder Я використовував у подібному рішенні. Чітка, лаконічна і прекрасно працює ... ура.
AJW

у цього алгоритму є недолік, як якщо ви клацаєте пробіл після набору тексту, тоді попередня довжина більша за довжину довжини
Marcin S.

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

Після додавання інструменту перегляду тексту у ваш редактор тексту:

yourEditText.addTextChangedListener(textWatcher);

Я сподіваюся, що він працює і на інших Android-пристроях (Samsung, LG тощо).


Пристрій HTC бажання (HTC є загальним, хоча :-P)
Джунаїд

якщо набрано один пробіл, то також порахуйте == 0
Bincy Baby

Блискуча відповідь № 1
брато

6
Це повністю не працює. count == 0 буде лише тоді, коли редакційний текст порожній!
Лев Droidcoder

@MarcAlexander Я не впевнений у цій відповіді, проте ви можете перевірити моє рішення у відповіді вище
Лео Droidcoder

5

Моє просте рішення, яке прекрасно працює. Вам слід додати прапор. Мій фрагмент коду:

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

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
Але, схоже, зворотний виклик НЕ "спрацьовував, навіть текст редагування був порожнім". Чи можете ви надати якийсь фрагмент з перехопленням видалення (повернення назад) для порожнього EditText? Заздалегідь дякую
камінь

1
ах, у мене є тест, коли я розробляю проект. У моєму випадку ввімкнено xiaomi mi8se, коли редакційний текст порожній і ви натискаєте видалити, зворотний виклик не запускається. Я буду шукати фрагмент цього речення.
Mạnh Hoàng Huynh

0

Подібне питання є і в Stackoverflow. Щоб EditTextотримати доступ до InputConnectionоб'єкта, який містить deleteSurroundingTextметод, вам потрібно переозначити . Це допоможе вам виявити подію видалення (backspace). Будь ласка, погляньте на рішення, яке я запропонував там Android - не вдається захопити зворотній простір / видалити натиск м'яким. клавіатура


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

0

Я також зіткнувся з тією ж проблемою в діалозі .. тому що я використовую setOnKeyListener .. Але я встановив повернення за замовчуванням 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. Я хотів виявити, чи була натиснута в моєму 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 на початку, і не дозволяти користувачу переміщувати курсор перед цим символом. Мій шлях полягає в тому, щоб вставити символ пробілу із зображенням розміру нульової ширини на ньому. Ось мій код.

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

І тепер ми закінчили ~ Ми можемо виявити ключову подію зворотної області, коли EditText не має фактичного вмісту, і користувач нічого не знатиме про нашу хитрість.


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

Як і попередні рішення, це може бути спровоковано автозавершенням / пропозиціями.
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);
    }

Перевірено це рішення - KEYCODE_DEL буде перенесено на активність, лише якщо редагування тексту не вирішить це самостійно. Наприклад, коли в editText немає тексту, або є якийсь текст, але курсор знаходиться на самому початку. Дуже смішно, що в моєму випадку мені потрібна саме така поведінка
Антон Кізема,

У моїй діяльності немає EditText, і я просто змушую клавіатуру відображатися програмно. Мені потрібно вхопити кожну м'яку клавіатурну клавішу, і це здається єдиним робочим рішенням. Інший - це перекриття методу dispatchKeyEvent. На жаль, починаючи з JellyBean, IME не надсилає KeyEvent для клавіші DELETE. developer.android.com/reference/android/view/KeyEvent.html
Bemipefe
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.