Android EditText delete(backspace) key event
How can I detect delete (backspace) key event for a editText? I've tried using TextWatcher, but when the editText is empty, when I press delete key, nothing happens. I want to detect delete key press foe an editText even if it has no text. 开发者_如何学JAVA
NOTE: onKeyListener
doesn't work for soft keyboards.
You can set OnKeyListener
for you editText
so you can detect any key press
EDIT: A common mistake we are checking KeyEvent.KEYCODE_BACK
for backspace
, but really it is KeyEvent.KEYCODE_DEL
(Really that name is very confusing! )
editText.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
//You can identify which key pressed by checking keyCode value with KeyEvent.KEYCODE_
if(keyCode == KeyEvent.KEYCODE_DEL) {
//this is for backspace
}
return false;
}
});
It's been a while since you asked but I just had the same issue. As already mentioned by Estel the problem with key listeners is that they only work with hardware keyboards. To do this with an IME (soft keyboard), the solution is a bit more elaborate.
The single method we actually want to override is sendKeyEvent
in the EditText
's InputConnection
class. This method is called when key events occur in an IME. But in order to override this, we need to implement a custom EditText
which overrides the onCreateInputConnection
method, wrapping the default InputConnection
object in a proxy class! :|
Sounds complicated, but here's the simplest example I could contrive:
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);
}
}
}
The line with the call to setRandomBackgroundColor
is where my special backspace action occurs. In this case, changing the EditText
's background colour.
If you're inflating this from XML remember to use the full package name as the tag:
<cc.buttfu.test.ZanyEditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/somefield"
></cc.buttfu.test.ZanyEditText>
This is just an addition to Idris's answer, adding in the override to deleteSurroundingText as well. I found more info on that here: 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);
}
}
}
Here is my easy solution, which works for all the API's:
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 ...
}
}
UPDATE 17.04.18 .
As pointed out in comments, this solution doesn't track the backspace press if EditText is empty (the same as most of the other solutions).
However, it's enough for most of the use cases.
P.S. If I had to create something similar today, I would do:
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());
}
}
Then just use it as a regular 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
}
});
I sent 2 days to find a solution and I figured out a working one :) (on soft keys)
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) {
}
}
After add the textwatcher to your EditText:
yourEditText.addTextChangedListener(textWatcher);
I hope it works on another android devices too (samsung, LG, etc).
My simple solution which works perfectly. You should to add a flag. My code snippet:
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
}
}
Example of creating EditText with TextWatcher
EditText someEdit=new EditText(this);
//create TextWatcher for our EditText
TextWatcher1 TW1 = new TextWatcher1(someEdit);
//apply our TextWatcher to EditText
someEdit.addTextChangedListener(TW1);
custom 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) {
}
}
for some one who's using Kotlin
addOnTextChanged
is not flexible enought to handle some cases (ex: detect if user press delete when edit text was empty)
setOnkeyListener
worked even soft keyboard or hardkeyboard! but just on some devices. In my case, it work on Samsung s8 but not work on Xiaomi mi8 se.
if you using kotlin, you can use crossline function doOnTextChanged
, it's the same as addOnTextChanged
but callback is triggered even edit text was empty.
NOTE: doOnTextChanged is a part of Android KTX library
Based on @Jiff ZanyEditText
here is WiseEditText
with 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;
}
}
This seems to be working for me :
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();
}
}
I am also faced same issue in Dialog.. because I am using setOnKeyListener.. But I set default return true. After change like below code it working fine for me..
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
}
});
My problem was, that I had custom Textwatcher
, so I didn't want to add OnKeyListener
to an EditText
as well as I didn't want to create custom EditText
. I wanted to detect if backspace was pressed in my afterTextChanged
method, so I shouldn't trigger my event.
This is how I solved this. Hope it would be helpful for someone.
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
}
}
I have tested @Jeff's solution on version 4.2, 4.4, 6.0. On 4.2 and 6.0, it works well. But on 4.4, it doesn't work.
I found an easy way to work around this problem. The key point is to insert an invisible character into the content of EditText at the begining, and don't let user move cursor before this character. My way is to insert a white-space character with an ImageSpan of Zero Width on it. Here is my code.
@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);
}
}
}
And we need custom an EditText which has a 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);
}
}
And the last step
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));
}
}
});
And now, we are done~ We can detect backspace key event when EditText has no actual content, and user will know nothing about our trick.
This question may be old but the answer is really simple using a 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();
}
I have found a really simple solution which works with a soft keyboard.
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
}
}
}
Belated but it may help new visitors, use TextWatcher()
instead will help alot and also it will work for both soft and hard keyboard as well.
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (charSequence.length() > 0) {
//Here it means back button is pressed and edit text is now empty
} else {
//Here edit text has some text
}
}
@Override
public void afterTextChanged(Editable editable) {
}
});
You could set a key listener on the activity, and in the callback method, you could detect which key the user hit. The code below is for your reference. Hope it helps.
//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);
}
精彩评论