how to get which character is get deleted on backspace in android - android

I am working with android 2.2.
How to know which character is get deleted on backspace when editing text in custom auto complete in android.
public boolean onKeyUp(int keyCode,KeyEvent msg){
if(keyCode == KeyEvent.KEYCODE_DEL)
{
// how to find here which character is get deleted
}
return false;
}

String prevText = "";
public boolean onKeyUp(int keyCode,KeyEvent msg){
if(keyCode == KeyEvent.KEYCODE_DEL)
{
int pos = myEditText.getSelectionStart();
char c = prevText.charAt(pos);
// c is deleted
}
prevText = myEditText.getText.toString();
return false;
}

You can use add a TextWatcher to AutoCompleteTextView with addTextChangeListener(TextWatcher).
You don't need to listen to onKeyUp() the various TextWatcher methods inform you if the user is adding or removing text.

The easiest way is just keep last character that you type
int lastKeyCode;
public boolean onKeyUp(int keyCode,KeyEvent msg){
if(keyCode == KeyEvent.KEYCODE_DEL)
{
// print lastKeyCode here
// how to find here which character is get deleted
}
lastKeyCode = keyCode;
return false;
}

Try this, working for me
editText.addTextChangedListener(new TextWatcher() {
String initialChar = null;
int initCursorPosition = 0;
#Override
public void onTextChanged(CharSequence charSequence, int start, int before, int after) {
char addedChar = 0;
int finalCursorPosition = editText.getSelectionStart();
if (finalCursorPosition - initCursorPosition > 0) {
addedChar = charSequence.charAt(finalCursorPosition - 1);
Log.d(TAG, "onTextChanged added: " + addedChar);
//added char
} else {
char delChar = initialChar.charAt(initCursorPosition - 1);
Log.d(TAG, "onTextChanged deletedChar: " + delChar);
//deleted char
}
}
#Override
public void beforeTextChanged(CharSequence charSequence, int start, int before, int after) {
Log.d(TAG, "textChange beforeTextChanged: " + charSequence);
Log.d(TAG, "textChange cursorPosition: " + editText.getSelectionStart());
initialChar = String.valueOf(charSequence);
initCursorPosition = editText.getSelectionStart();
}
#Override
public void afterTextChanged(Editable arg0) {
}
});

(Edittext).setOnKeyListener(new OnKeyListener()
{
public boolean onKey(View v, int keyCode, KeyEvent event)
{
if(event.getKeyCode()==67)
{
if((EditText).getText().toString().length()>0)
{
int pos = (Edittext).getSelectionStart();
Char c = (EditText).getText().toString.charAt(pos);
Toast.makeText(getApplicationontext(),String.valueOf(c),Toast.LENGTH_SHORT).show();
}
}
return false;
}
});
I think it helps you

I know this is a very late answer, but this would be effective for future users.
First the KeyEvent.KEYCODE_DEL doesn't work for soft keyboard in latest android versions, so you have to create a custom EditText to handle that.
So we'll create a class to handle all null type in LatinIME
import android.text.SpannableStringBuilder;
public class EditableAccomodatingLatinIMETypeNullIssues extends SpannableStringBuilder {
EditableAccomodatingLatinIMETypeNullIssues(CharSequence source) {
super(source);
}
public static CharSequence ONE_UNPROCESSED_CHARACTER = "/";
#Override
public SpannableStringBuilder replace(final int
spannableStringStart, final int spannableStringEnd, CharSequence replacementSequence,
int replacementStart, int replacementEnd) {
if (replacementEnd > replacementStart) {
super.replace(0, length(), "", 0, 0);
return super.replace(0, 0, replacementSequence, replacementStart, replacementEnd);
}
else if (spannableStringEnd > spannableStringStart) {
super.replace(0, length(), "", 0, 0);
return super.replace(0, 0, ONE_UNPROCESSED_CHARACTER, 0, 1);
}
return super.replace(spannableStringStart, spannableStringEnd,
replacementSequence, replacementStart, replacementEnd);
}
}
Then we'll go ahead to create another class to handle the InputConnection of the custom EditText to be created
import android.os.Build;
import android.text.Editable;
import android.text.Selection;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.BaseInputConnection;
public class InputConnectionAccomodatingLatinIMETypeNullIssues extends BaseInputConnection {
Editable myEditable = null;
public InputConnectionAccomodatingLatinIMETypeNullIssues(View targetView, boolean fullEditor) {
super(targetView, fullEditor);
}
#Override
public Editable getEditable() {
if(Build.VERSION.SDK_INT >= 14) {
if(myEditable == null) {
myEditable = new EditableAccomodatingLatinIMETypeNullIssues(
EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER);
Selection.setSelection(myEditable, 1);
}
else {
int myEditableLength = myEditable.length();
if(myEditableLength == 0) {
myEditable.append(
EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER);
Selection.setSelection(myEditable, 1);
}
}
return myEditable;
}
else {
return super.getEditable();
}
}
#Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
if((Build.VERSION.SDK_INT >= 14) // && (Build.VERSION.SDK_INT < 19)
&& (beforeLength == 1 && afterLength == 0)) {
return super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
&& super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
}
else {
return super.deleteSurroundingText(beforeLength, afterLength);
}
}
}
Then creating the custom EditText
import android.content.Context;
import android.text.InputType;
import android.util.AttributeSet;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
public class CustomEditText extends androidx.appcompat.widget.AppCompatEditText {
public CustomEditText(#NonNull #NotNull Context context) {
super(context);
}
public CustomEditText(#NonNull #NotNull Context context, #Nullable #org.jetbrains.annotations.Nullable AttributeSet attrs) {
super(context, attrs);
}
public CustomEditText(#NonNull #NotNull Context context, #Nullable #org.jetbrains.annotations.Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
InputConnectionAccomodatingLatinIMETypeNullIssues baseInputConnection =
new InputConnectionAccomodatingLatinIMETypeNullIssues(this, false);
outAttrs.actionLabel = null;
outAttrs.inputType = InputType.TYPE_NULL;
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
return baseInputConnection;
}
}
So in your .xml file, you can say something like
<your.package.name.CustomEditText
<!set all required attributes-->
/>
Now in your java code, you have to listen for the Backspace click event and get the character to be deleted before it's deleted.
editText.setOnKeyListener(new View.OnKeyListener(){
public boolean onKey(View v, int keyCode, KeyEvent event){
if(event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_DEL) {
int selStart = editText.getSelectionStart();
int selEnd = editText.getSelectionEnd();
//incase if some text in the textbox are selected, selStart and selEnd will
//return the positions of both selected ends and you can get the text(s) deleted.
//check if the selEnd is at 0, and if true, then there's not point going further
if(selEnd == 0)
return false;
//if there is no texts selected, then reduce selStart by one to get the text to be deleted
if(selStart == selEnd)
selStart -= 1;
String text = editText.getText().toString();
//now this will get you char or chars to be deleted
String charDeleted = text.substring(selStart, selEnd);
//So before returning false to deleted the char,
//let me answer the question asked also on the
//comment section in Bobs' answer
//which is to find the color code of the deleted char
//Note, this is for only a single char, if you want to
//handle for multiple chars, then you'll have to split the
//deleted chars into an array and get the color codes for each
//This will get all HTML text within the deleted char including the color code
String htmlText = Html.toHtml((Spanned) editText.getText().subSequence(selStart, selEnd));
//Now you have to use a HTML parser, and I'm using JSoup
//Don't forget to implement the JSoup library into your project
Document doc = Jsoup.parse(htmlText, "UTF-8");
//Here I'm selecting a SPAN html tag, hoping that the char should be in a SPAN
//If it's in a FONT or any other tag, then you select the tag instead and also
//the attribute containing the color code
Elements element = doc.select("span");
//Attribute containing the color code for SPAN is the STYLE
String style = element.attr("style");
//So the style string would be giving you something like "color:#AABBCC;"
//Where #AABBCC is the color code of the deleted char
}
return false;
}
});

Try this
edittext.addTextChangeListener(new TextWatcher{
#override
public void afterTextChanged(Editable s){
String changedtext = s.toString();
}
#override
public void beforeTextChanged (CharSequence s, int start, int count, int after){}
#override
public void onTextChanged(CharSequence s, int start, int before, int count){}
});

Related

OTP View with multiple edit text and soft keyboard delete Action

I am implementing a custom view for OTP of varying length upto 6 digits. I have extended a LinearLayout and use multiple edit text as its child view. Each edit text holds one digit. I want to implement the delete action from the soft keyboard for the above custom view. The following is the code for the OTP custom view.
public class OTPEditText extends LinearLayout {
private int mDigitSpacing = 8; // Space between digits
private int mDigitNumber = 6; // Number of digits
private int mDigitSize = 28; // Font size of the digits
private ArrayList<EditText> mEditTexts; // List of edit text each holding one digit
private OnCompleteListener mCompleteListener; //when all the edit text gets one digit each
public OTPEditText(Context context) {
super(context);
}
public OTPEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public OTPEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* Add the required number of Edit Texts
* #param number - number of digits
*/
public void setDigitNumber(int number){
this.mDigitNumber = number;
addViews();
}
public void setOnCompleteListener(OnCompleteListener listener) {
this.mCompleteListener = listener;
}
private void addViews() {
removeAllViews();
mEditTexts = new ArrayList<>();
for(int i = 0; i < mDigitNumber; i++){
EditText editText = new EditText(getContext());
//Set the necessary attributes
editText.addTextChangedListener(new GenericTextWatcher(i));
mEditTexts.add(editText);
addView(editText);
}
requestLayout();
if(mEditTexts.size() > 0) {
mEditTexts.get(0).requestFocus();
}
}
/**
* similar to setText of an edit text, but
* set one digit each to the edit text
* #param s - string for the edit text
*/
public void setText(String s){
if(s.length() > mDigitNumber){
s = s.substring(0, mDigitNumber);
}
int i;
for(i = 0; i < s.length(); i++){
mEditTexts.get(i).setText(s.charAt(i));
}
for(; i < mEditTexts.size(); i++){
mEditTexts.get(i).setText("");
}
}
/**
* Similar to the getText of an edit text,
* concatenates the text from each edit text
* #return - concatenated string from each edit text
*/
public String getText() {
String text = "";
if(!Utils.isEmptyList(mEditTexts)) {
for (EditText editText : mEditTexts){
text += editText.getText().toString();
}
}
return text;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event){
return true;
}
/**
* Called whenever onClick of the View is called. Simulates the click event of
* the required edit text.
*/
public void doClick() {
if(!Utils.isEmptyList(mEditTexts)){
for(EditText editText : mEditTexts){
if(editText.getText().toString().equals("")){
editText.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN , 0, 0, 0));
editText.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_UP , 0, 0, 0));
return;
}
}
mEditTexts.get(mEditTexts.size()-1).dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN ,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mDigitSize,
getResources().getDisplayMetrics()), 0, 0));
mEditTexts.get(mEditTexts.size()-1).dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_UP ,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mDigitSize,
getResources().getDisplayMetrics()), 0, 0));
}
}
public interface OnCompleteListener {
void onComplete();
}
// Generic edit text watcher
public class GenericTextWatcher implements TextWatcher {
private int index;
public GenericTextWatcher(int index){
this.index = index;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
if(s.toString().length() >= 1){
if(index +1 < mEditTexts.size()){
mEditTexts.get(index + 1).requestFocus();
} else if(index == mEditTexts.size() - 1 && mCompleteListener != null){
mCompleteListener.onComplete();
}
}
}
}
}
edOtp1.addTextChangedListener(new OtpTextWatcher(edOtp1));
edOtp2.addTextChangedListener(new OtpTextWatcher(edOtp2));
edOtp3.addTextChangedListener(new OtpTextWatcher(edOtp3));
edOtp4.addTextChangedListener(new OtpTextWatcher(edOtp4));
create this class that handle text on addition or deletion.
private class OtpTextWatcher implements TextWatcher
{
private View view;
OtpTextWatcher(View view) {
this.view = view;
}
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
String text = charSequence.toString();
switch (view.getId()) {
case R.id.edOtp1:
if (text.length() == 1)
edOtp2.requestFocus();
edOtp2.setSelection(edOtp2.getText().length());
else{
edOtp1.requestFocus();
}
break;
case R.id.edOtp2:
if (text.length() == 0) {
edOtp1.requestFocus();
edOtp1.setSelection(edOtp1.getText().length());
}
break;
case R.id.edOtp3:
if (text.length() == 0) {
edOtp2.requestFocus();
edOtp2.setSelection(edOtp2.getText().length());
}
break;
case R.id.edOtp4:
if (text.length() == 0) {
edOtp3.requestFocus();
edOtp3.setSelection(edOtp3.getText().length());
}
break;
default:
break;
}
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
String text = editable.toString();
switch (view.getId()) {
case R.id.edOtp1:
if (text.length() == 1)
edOtp2.requestFocus();
else
edOtp1.setSelection(edOtp1.getText().length());
break;
case R.id.edOtp2:
if (text.length() == 1)
edOtp3.requestFocus();
else if (text.length() == 0) {
edOtp1.requestFocus();
edOtp1.setSelection(edOtp1.getText().length());
}
break;
case R.id.edOtp3:
if (text.length() == 1)
edOtp4.requestFocus();
else if (text.length() == 0) {
edOtp2.requestFocus();
edOtp2.setSelection(edOtp2.getText().length());
}
break;
case R.id.edOtp4:
if (text.length() == 0) {
edOtp3.requestFocus();
edOtp3.setSelection(edOtp3.getText().length());
}
break;
default:
break;
}
}
}
I created a gist here https://gist.github.com/ShivamPokhriyal/8d0cf4aef062e6c59d00c04c53e03158 which you can simply copy paste in your project.
It creates a custom OTPEditText class which handles shifting the focus to next or previous edittext when user types in or deletes and also handles the paste event when user long presses and pastes the otp in the editText. All this can be done in the xml only. No need to pollute your activity with these stuff.
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* This class handles otp input in multiple edittexts.
* It will move focus to next edittext, if available, when user enters otp.
* And it will move focus to the previous edittext, if available, when user deletes otp.
* It will also delegate the paste option, if user long presses and pastes a string into the otp input.
*
* <b>XML attributes</b>
*
* #attr ref your_package_name.R.styleable#OTPView_nextView
* #attr ref your_package_name.R.styleable#OTPView_prevView
*
* #author $|-|!˅#M
*/
public class OTPEditText extends androidx.appcompat.widget.AppCompatEditText {
#Nullable
private View nextView;
#Nullable
private View previousView;
// Unfortunately getParent returns null inside the constructor. So we need to store the IDs.
private int nextViewId;
private int previousViewId;
#Nullable
private Listener listener;
private static final int NO_ID = -1;
public interface Listener {
void onPaste(String s);
}
public OTPEditText(#NonNull Context context) {
super(context);
}
public OTPEditText(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public OTPEditText(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
public void setListener(Listener listener) {
this.listener = listener;
}
/**
* Called when a context menu option for the text view is selected. Currently
* this will be one of {#link android.R.id#selectAll}, {#link android.R.id#cut},
* {#link android.R.id#copy}, {#link android.R.id#paste} or {#link android.R.id#shareText}.
*
* #return true if the context menu item action was performed.
*/
#Override
public boolean onTextContextMenuItem(int id) {
if (id == android.R.id.paste) {
ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
// Examines the item on the clipboard. If getText() does not return null, the clip item contains the
// text. Assumes that this application can only handle one item at a time.
ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);
// Gets the clipboard as text.
CharSequence pasteData = item.getText();
if (listener != null && pasteData != null) {
listener.onPaste(pasteData.toString());
return true;
}
}
return super.onTextContextMenuItem(id);
}
#Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
// If we've gotten focus here
if (focused && this.getText() != null) {
this.setSelection(this.getText().length());
}
}
private void init(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.OTPView, 0, 0);
nextViewId = typedArray.getResourceId(R.styleable.OTPView_nextView, NO_ID);
previousViewId = typedArray.getResourceId(R.styleable.OTPView_prevView, NO_ID);
typedArray.recycle();
this.setOnKeyListener((v, keyCode, event) -> {
if (event.getAction()!= KeyEvent.ACTION_DOWN) {
return true;
}
//You can identify which key pressed by checking keyCode value with KeyEvent.KEYCODE_
if(keyCode == KeyEvent.KEYCODE_DEL) {
// Back pressed. If we have a previous view. Go to it.
if (getPreviousView() != null) {
getPreviousView().requestFocus();
return true;
}
}
return false;
});
this.addTextChangedListener(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) { }
#Override
public void afterTextChanged(Editable s) {
if (s.length() == 1 && getNextView() != null) {
getNextView().requestFocus();
} else if (s.length() == 0 && getPreviousView() != null) {
getPreviousView().requestFocus();
}
}
});
// Android 3rd party keyboards show the copied text into the suggestion box for the user.
// Users can then simply tap on that suggestion to paste the text on the edittext.
// But I don't know of any API that allows handling of those paste actions.
// Below code will try to tell those keyboards to stop showing those suggestion.
this.setInputType(EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS | EditorInfo.TYPE_CLASS_NUMBER);
}
private View getNextView() {
if (nextView != null) {
return nextView;
}
if (nextViewId != NO_ID && getParent() instanceof View) {
nextView = ((View) getParent()).findViewById(nextViewId);
return nextView;
}
return null;
}
private View getPreviousView() {
if (previousView != null) {
return previousView;
}
if (previousViewId != NO_ID && getParent() instanceof View) {
previousView = ((View) getParent()).findViewById(previousViewId);
return previousView;
}
return null;
}
}
The gist also includes the xml and java code that you can directly add to your activity.

Android edittext append and remove append

I'm trying to make editText, where I am inserting some text. After each three characters,I want to insert dash.
Example:
Type: 123
Result:123-
Now when cursor is behind dash and you press delete, I want to delete dash and character behind dash.
For example:
123-
result after delete key: 12.
How to do it. Thank you for advice.
EDIT
my code is:
EditText editText;
boolean keyDel = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = (EditText) findViewById(R.id.editText);
editText.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DEL) {
keyDel = true;
}
return keyDel;
}
});
editText.addTextChangedListener(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) {
String str = s.toString();
System.out.println(str.length());
if (str.length() == 3) {
str = str + "-";
} else if (str.length() == 7) {
str = str + "-";
} else if (str.length() % 4 == 0 && keyDel == true) {
str = str.substring(0, str.length() - 2);
} else {
return;
}
editText.setText(str);
editText.setSelection(editText.getText().length());
}
#Override
public void afterTextChanged(Editable s) {
}
});
}
I found Android 4.4.2 and higer doesn´t support keyevent.
onTextChanged is called everytime you add and remove something. So if your String has length 3, you add your - and the new length is 4. If you press delete (new length is 3 again), onTextChanged is called and - is added again. SO only add something if nothing has been removed from the text.
if (count > before) {
if (count == 3 || count == 7) {
str = str + "-";
} else {
return;
}
input.setText(str);
input.setSelection(input.getText().length());
}
I was inspired by this answer to achieve what you want:
String mTextValue;
Character mLastChar = '\0'; // init with empty character
int mKeyDel;
myEditText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
boolean flag = true;
String eachBlock[] = myEditText.getText().toString().split("-");
for (int i = 0; i < eachBlock.length; i++) {
if (eachBlock[i].length() > 4) {
flag = false;
}
}
if (flag) {
myEditText.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DEL)
mKeyDel = 1;
return false;
}
});
if (mKeyDel == 0) {
if (((myEditText.getText().length() + 1) % 4) == 0) {
myEditText.setText(myEditTex.getText() + "-");
myEditText.setSelection(myEditText.getText().length());
}
mTextValue = myEditText.getText().toString();
} else {
mTextValue = myEditText.getText().toString();
if (mLastChar.equals('-')) {
mTextValue = mTextValue.substring(0, mTextValue.length() - 1);
myEditText.setText(mTextValue);
myEditText.setSelection(mTextValue.length());
}
mKeyDel = 0;
}
} else {
myEditText.setText(mTextValue);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (s.length()>0) {// save the last char value
mLastChar = s.charAt(s.length() - 1);
} else {
mLastChar = '\0';
}
}
#Override
public void afterTextChanged(Editable s) {}
});
I'm rather new to android programming myself. Nevertheless, I think that everytime you call "setText" you are trigging a new onTextChanged event.
In my app I remove the listener, set the text and then add the listener again in order to avoid this problem. But for doing this you'll have to save the reference to the TextWatcher.
I.e. given your Activity extends TextWatcher:
edittext.removeTextChangedListener(this);
editText.setText(str);
edittext.addTextChangedListener(this);
You can also put the cursor at the end using:
myedittext.append("");

show compound drawable to EditText on event

In my application in Activity I want to set EditText as I click inside(focus) EditText and type a key clear button should appear on right side of EditText and when EditText is empty that clear button have to removed.But it is not showing to me..Which event should I have to implement here..?onTouch or onFocusChange or addTextChangedListener and also what code be there..? following code I have done in activity...
in Activity :
clear = getResources().getDrawable(R.drawable.round_clear);
clear.setBounds(0, 0, clear.getIntrinsicWidth(), clear.getIntrinsicHeight());
and event as
#Override
public void onFocusChange(View v, boolean hasFocus)
{
switch (v.getId())
{
case R.id.uIDEditText:
if(hasFocus && !uIDEditText.getText().toString().isEmpty())
uIDEditText.setCompoundDrawables(null, null, clear, null);
else
uIDEditText.setCompoundDrawables(null, null, null, null);
break;
case R.id.pwdEditText:
if(hasFocus && !pwdEditText.getText().toString().isEmpty())
pwdEditText.setCompoundDrawables(null, null, clear, null);
else
pwdEditText.setCompoundDrawables(null, null, null, null);
break;
}
}
another event is :
#Override
public boolean onTouch(View v, MotionEvent event)
{
switch (v.getId())
{
case R.id.uIDEditText:
final int x = (int)event.getX();
final int y = (int)event.getY();
if(event.getAction() == MotionEvent.ACTION_UP && clear!=null) {
Rect rBounds = clear.getBounds();
int n1 = v.getRight();
int n2 = v.getRight()+rBounds.width();
int n3 = v.getPaddingTop();
int n4 = v.getHeight()-v.getPaddingBottom();
if(x>=(n1) && x<=(n2) && y>=n3 && y<=(n4))
{
uIDEditText.setText("");
event.setAction(MotionEvent.ACTION_CANCEL);
}
}
break;
}
}
I sovled it...created following code
public class CustomEditText extends EditText {
private Drawable dRight;
private Rect rBounds;
CustomEditText(Context context,AttributeSet attributeSet){
super(context,attributeSet);
}
#Override
public void setCompoundDrawables(Drawable left, Drawable top,
Drawable right, Drawable bottom) {
/*if (left != null) {
dLeft = left;
}*/
if (right != null) {
dRight = right;
}
super.setCompoundDrawables(left, top, right, bottom);
}
#Override
public void addTextChangedListener(TextWatcher watcher) {
super.addTextChangedListener(watcher);
}
#Override
protected void onTextChanged(CharSequence text, int start,
int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
if(this.getText().toString().length()>0)
this.setCompoundDrawablesWithIntrinsicBounds(null, null, dRight, null);
else
this.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
}
#Override
protected void finalize() throws Throwable {
dRight = null;
rBounds = null;
super.finalize();
}
}
and added in xml:
<com.example.screen.CustomEditText
android:id="#+id/uIDEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableRight="#drawable/round_clear"
android:textColor="#ffffff" />
in Activity (edittext ontouch listener):
uIDEditText.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() != MotionEvent.ACTION_UP)
return false;
if (event.getX() > uIDEditText.getWidth() - clear.getIntrinsicWidth())
{
uIDEditText.setText("");
event.setAction(MotionEvent.ACTION_CANCEL);
}
return false;
}
});
What you want is here .
Use Textwatcher functionality for edittext.
package com.example.editwatch;
import android.os.Bundle;
import android.app.Activity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
public class MainActivity extends Activity {
EditText edittext_search;
ImageView imageView1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edittext_search=(EditText)findViewById(R.id.editText1);
imageView1=(ImageView)findViewById(R.id.imageView1);
edittext_search.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
if(edittext_search.getText().length()>0)
{
imageView1.setVisibility(View.VISIBLE);
}
else
{
imageView1.setVisibility(View.GONE);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});
}
}

Custom format edit text input android to accept credit card number

how to make edit text accept input in format
4digitnumber-4dignumber-4dignumber-4dignumber
The code
text.addTextChangedListener(new TextWatcher() {
int len = 0;
String string ;
#Override
public void afterTextChanged(Editable s) {
text.setOnKeyListener(new OnKeyListener()
{ public boolean onKey(View v, int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_DEL)
{
}
else{
string = text.getText().toString();
len = string.length()+1;
if(len%5==0){text.append("-");}
}
return false; } });
}
});
works fine upon adding, but deleting or editing causes problem.
Now this works fine for soft/hard keyboard for all delete/edit ops.
tx 4 ur help..
package com.and;
import android.app.Activity;
import android.app.AlertDialog;
import android.inputmethodservice.KeyboardView;
import android.os.Bundle;
import android.telephony.PhoneNumberFormattingTextWatcher;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextWatcher;
import android.text.format.Formatter;
import android.text.method.NumberKeyListener;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.widget.EditText;
import android.widget.Toast;
public class ccformat extends Activity {
String a;
int keyDel;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final EditText text = (EditText) findViewById(com.and.R.id.editText1);
text.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
boolean flag = true;
String eachBlock[] = text.getText().toString().split("-");
for (int i = 0; i < eachBlock.length; i++) {
if (eachBlock[i].length() > 4) {
flag = false;
}
}
if (flag) {
text.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DEL)
keyDel = 1;
return false;
}
});
if (keyDel == 0) {
if (((text.getText().length() + 1) % 5) == 0) {
if (text.getText().toString().split("-").length <= 3) {
text.setText(text.getText() + "-");
text.setSelection(text.getText().length());
}
}
a = text.getText().toString();
} else {
a = text.getText().toString();
keyDel = 0;
}
} else {
text.setText(a);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
}
});
}
}
This is working:
public class EditTextSample extends Activity {
// This regexp has to be improved, it does not detect case where you have
// more than 4 digits in a middle group like: 1234-12345-123
static final Pattern CODE_PATTERN = Pattern.compile("([0-9]{0,4})|([0-9]{4}-)+|([0-9]{4}-[0-9]{0,4})+");
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_text_sample);
final EditText editText = (EditText) findViewById(R.id.input);
editText.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
Log.w("", "input" + s.toString());
if (s.length() > 0 && !CODE_PATTERN.matcher(s).matches()) {
String input = s.toString();
String numbersOnly = keepNumbersOnly(input);
String code = formatNumbersAsCode(numbersOnly);
Log.w("", "numbersOnly" + numbersOnly);
Log.w("", "code" + code);
editText.removeTextChangedListener(this);
editText.setText(code);
// You could also remember the previous position of the cursor
editText.setSelection(code.length());
editText.addTextChangedListener(this);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
private String keepNumbersOnly(CharSequence s) {
return s.toString().replaceAll("[^0-9]", ""); // Should of course be more robust
}
private String formatNumbersAsCode(CharSequence s) {
int groupDigits = 0;
String tmp = "";
for (int i = 0; i < s.length(); ++i) {
tmp += s.charAt(i);
++groupDigits;
if (groupDigits == 4) {
tmp += "-";
groupDigits = 0;
}
}
return tmp;
}
});
}
}
If you want to just group visually the numbers, but you don't want to alter the value of the EditText adding dashes, you can use this Span approach:
EditText editText = findViewById(R.id.editText);
editText.addTextChangedListener(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) {}
#Override
public void afterTextChanged(Editable editable) {
Object[] paddingSpans = editable.getSpans(0, editable.length(), DashSpan.class);
for (Object span : paddingSpans) {
editable.removeSpan(span);
}
addSpans(editable);
}
private static final int GROUP_SIZE = 4;
private void addSpans(Editable editable) {
final int length = editable.length();
for (int i = 1; i * (GROUP_SIZE) < length; i++) {
int index = i * GROUP_SIZE;
editable.setSpan(new DashSpan(), index - 1, index,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
});
where the DashSpan class looks like this:
/**
* A {#link ReplacementSpan} used for spacing in {#link android.widget.EditText}
* to space things out. Adds '-'s
*/
public class DashSpan extends ReplacementSpan {
#Override
public int getSize(#NonNull Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) {
float padding = paint.measureText("-", 0, 1);
float textSize = paint.measureText(text, start, end);
return (int) (padding + textSize);
}
#Override
public void draw(#NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y,
int bottom, #NonNull Paint paint) {
canvas.drawText(text.subSequence(start, end) + "-", x, y, paint);
}
}
This way you will have visually the grouping using the dashes, but the getText() will return the text without any grouping.
To force only numbers you can add the attributes android:digits="0123456789" and android:inputType="number" to the EditText.
This solution is based on the code of this library.
In my case below code is working fine.
editTextCreditCard.addTextChangedListener(new FourDigitCardFormatWatcher());
Add custom class for TextWatcher.
public class FourDigitCardFormatWatcher implements TextWatcher {
private static final char space = ' ';
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
if (s.length() > 0 && (s.length() % 5) == 0) {
final char c = s.charAt(s.length() - 1);
if (space == c) {
s.delete(s.length() - 1, s.length());
}
}
if (s.length() > 0 && (s.length() % 5) == 0) {
char c = s.charAt(s.length() - 1);
if (Character.isDigit(c) && TextUtils.split(s.toString(), String.valueOf(space)).length <= 3) {
s.insert(s.length() - 1, String.valueOf(space));
}
}
}
}
Hope this would help you.
It works in all cases, when you insert or remove a character, the format will always be right. Make sure you set
android:inputType="number"
/
myEditText.addTextChangedListener(new TextWatcher() {
private final String space = "-"; // you can change this to whatever you want
private final Pattern pattern = Pattern.compile("^(\\d{4}"+space+"{1}){0,3}\\d{1,4}$"); // check whether we need to modify or not
#Override
public void onTextChanged(CharSequence s, int st, int be, int count) {
String currentText = myEditText.getText().toString();
if (currentText.isEmpty() || pattern.matcher(currentText).matches())
return; // no need to modify
String numbersOnly = currentText.trim().replaceAll("[^\\d.]", "");; // remove everything but numbers
String formatted = "";
for(int i = 0; i < numbersOnly.length(); i += 4)
if (i + 4 < numbersOnly.length())
formatted += numbersOnly.substring(i,i+4)+space;
else
formatted += numbersOnly.substring(i);
myEditText.setText(formatted);
myEditText.setSelection(myEditText.getText().toString().length());
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
#Override
public void afterTextChanged(Editable e) {}
});
It seems to me the answers presented here do not work properly with delete, delete from the middle operations, etc.
Here is my code. It doesn't restrict the length of input, but seems to be ok with various insertions and deletions:
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.widget.EditText;
public class HyphenDelimitTextWatcher implements TextWatcher {
EditText mEditText;
boolean mInside = false;
boolean mWannaDeleteHyphen = false;
boolean mKeyListenerSet = false;
final static String MARKER = "|"; // filtered in layout not to be in the string
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if(!mKeyListenerSet) {
mEditText.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
try {
mWannaDeleteHyphen = (keyCode == KeyEvent.KEYCODE_DEL
&& mEditText.getSelectionEnd() - mEditText.getSelectionStart() <= 1
&& mEditText.getSelectionStart() > 0
&& mEditText.getText().toString().charAt(mEditText.getSelectionEnd() - 1) == '-');
} catch (IndexOutOfBoundsException e) {
// never to happen because of checks
}
return false;
}
});
mKeyListenerSet = true;
}
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (mInside) // to avoid recursive calls
return;
mInside = true;
int currentPos = mEditText.getSelectionStart();
String string = mEditText.getText().toString().toUpperCase();
String newString = makePrettyString(string);
mEditText.setText(newString);
try {
mEditText.setSelection(getCursorPos(string, newString, currentPos, mWannaDeleteHyphen));
} catch (IndexOutOfBoundsException e) {
mEditText.setSelection(mEditText.length()); // last resort never to happen
}
mWannaDeleteHyphen = false;
mInside = false;
}
#Override
public void afterTextChanged(Editable s) {
}
private String makePrettyString(String string) {
String number = string.replaceAll("-", "");
boolean isEndHyphen = string.endsWith("-") && (number.length()%4 == 0);
return number.replaceAll("(.{4}(?!$))", "$1-") + (isEndHyphen ?"-":"");
}
private int getCursorPos(String oldString, String newString, int oldPos, boolean isDeleteHyphen) {
int cursorPos = newString.length();
if(oldPos != oldString.length()) {
String stringWithMarker = oldString.substring(0, oldPos) + MARKER + oldString.substring(oldPos);
cursorPos = (makePrettyString(stringWithMarker)).indexOf(MARKER);
if(isDeleteHyphen)
cursorPos -= 1;
}
return cursorPos;
}
public HyphenDelimitTextWatcher(EditText editText) {
mEditText = editText;
}
}
Usage:
mSomeEditText.addTextChangedListener(new HyphenDelimitTextWatcher(mSomeEditText));
if you neeed this efect,ou can use this code in EditText
Here is a formatting regex used to show card details in format XXXX XXXX XXXX XXXX
etCreditCardNumber.addTextChangedListener(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) {
etCreditCardNumber.setFloatingLabel(MaterialEditText.FLOATING_LABEL_HIGHLIGHT);
String initial = s.toString();
// remove all non-digits characters
String processed = initial.replaceAll("\\D", "");
// insert a space after all groups of 4 digits that are followed by another digit
processed = processed.replaceAll("(\\d{4})(?=\\d)(?=\\d)(?=\\d)", "$1 ");
//Remove the listener
etCreditCardNumber.removeTextChangedListener(this);
int index = etCreditCardNumber.getSelectionEnd();
if (index == 5 || index == 10 || index == 15)
if (count > before)
index++;
else
index--;
//Assign processed text
etCreditCardNumber.setText(processed);
try {
etCreditCardNumber.setSelection(index);
} catch (Exception e) {
e.printStackTrace();
etCreditCardNumber.setSelection(s.length() - 1);
}
//Give back the listener
etCreditCardNumber.addTextChangedListener(this);
}
#Override
public void afterTextChanged(Editable s) {
}
});

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

Categories

Resources