Reputation: 859
I am trying to limit an edit text of one character, I have a CustomTextWatcher class which handle all text changes events. Lets say if i write one letter, the focus will automatically jump to the next edit text, but if I touch on the editText to getFocus if cursor is before previous letter it allows me to write one letter being 2 then, i tried clearing edit text in beforeTextChanged but it crashes the app, any ideas of how to approach this problem?
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
EditText e = (EditText) v;
if(e.getText().toString().length() == 0 || e.getText().toString().equals(""))
e.setText(s); // if true means there is no previous text so i can just use whatever comes in s charSequence
else
e.setText(""); // if false, means there is some text, so i want to clean it up first and write new character
Log.d(TAG, "beforeTextChanged: ");
}
UPDATE : ok so after following the recomendations this is what I've got:
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
hasToClear = false;
e = (EditText) v;
if(e.getText().toString().length() > 0 || !e.getText().toString().equals("")) {
previousChar = e.getText().toString(); // if either true, the box was not empty
hasToClear = true;
}
Log.d(TAG, "beforeTextChanged: ");
}
@Override
public void afterTextChanged(Editable s) {
InputFilter[] editFilters = s.getFilters();
InputFilter[] newFilters = new InputFilter[editFilters.length + 2];
System.arraycopy(editFilters, 0, newFilters, 0, editFilters.length);
newFilters[editFilters.length] = new InputFilter.AllCaps();
if(!isNumber)
newFilters[editFilters.length+1] = charFilter;
else
newFilters[editFilters.length + 1] = digitFilter;
s.setFilters(newFilters);
if(isAllowed) {
if (i == NUM_DIGITS) {
edList[5].clearFocus();
onTextFinishedListener.onPlateFinished(true);
} else {
edList[i].selectAll();
edList[i].requestFocus();
}
}
String stringToReplace = "";
if(previousChar != null) { // find which caracter is diferent as there is only one allowed
for (int x = 0; x < s.length(); x++) {
if (s.charAt(x) != previousChar.charAt(0)) {
stringToReplace = String.valueOf(s.charAt(x));
break;
} else stringToReplace = String.valueOf(previousChar);
}
}
if(hasToClear){ // if true clear
e.removeTextChangedListener(this);
e.setText("");
e.setText(stringToReplace);
e.addTextChangedListener(this);
}
}
if user clicks on, before or after any character the next input user does, must replace to previous character, this is happening with before and on character, but is not happening with after
Upvotes: 0
Views: 2460
Reputation: 859
After dealing with different approaches, I decided to walk away from input filters, lets say they did not behave in the way I wish they would have, so in order to not break the UI by just blocking Characters from the xml, I use two methods:
private boolean validateLetter(Character s){
if(s == null)
return false;
if(!Character.isLetter(s)) { //if it isn't a letter, throw message
Toast.makeText(mContext, "Character not allowed", Toast.LENGTH_SHORT).show();
return false;
} else {
return true;
}
}
private boolean validateNumber(Character s){
if(s == null)
return false;
if(!Character.isDigit(s)) { // if it isn't a digit, throw message-
Toast.makeText(mContext, "Character not allowed", Toast.LENGTH_SHORT).show();
return false;
} else {
return true;
}
}
and the TextWatcher looks as it follows:
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
hasToClear = false;
isAllowed = false;
e = (EditText) v;
if(e.getText().toString().length() > 0 || !e.getText().toString().equals("")) {
previousChar = e.getText().toString(); //store previous char if there is any
hasToClear = true; // rise flag for clearing field in after event
}
Log.d(TAG, "beforeTextChanged: ");
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
isNumber = false;
String[] data= v.getTag().toString().split(";"); //recieve the type of input for the edit text and its position in the layout by xml tag
String type = data[0];
pos = Integer.parseInt(data[1]);
if(type.equals("number"))
isNumber = true;
}
@Override
public void afterTextChanged(Editable s) {
Character stringToReplace = null;
if(previousChar != null) { //
for (int x = 0; x < s.length(); x++) {
if (s.charAt(x) != previousChar.charAt(0)) { //this bucle find the different character in the edit text
stringToReplace = s.charAt(x);
break;
} else stringToReplace = previousChar.charAt(0);
}
}
if(isNumber){
if(validateNumber(stringToReplace)){
isAllowed = true;
}else isAllowed = false;
}else
if(validateLetter(stringToReplace)){
isAllowed = true;
} else isAllowed = false;
if(isAllowed) {
if(hasToClear){
e.removeTextChangedListener(this); //remove listener
e.setText(""); //clear field
e.setText(String.valueOf(stringToReplace)); //add new value
e.addTextChangedListener(this); //add listener back
}
if (pos == NUM_DIGITS) { //jump to next editText until it reaches NUM_DIGITS, after will notify that it has finished filling all editText
edList[5].clearFocus();
onTextFinishedListener.onPlateFinished(true);
} else {
edList[pos].selectAll();
edList[pos].requestFocus();
}
} else {
if(previousChar != null) {
e.removeTextChangedListener(this);
e.setText(String.valueOf(previousChar));
e.addTextChangedListener(this);
}
}
isAllowed = false;
}
Special thanks to
@Veneet Reddy
Upvotes: 0
Reputation: 6363
Why are you using that code, you can use this.
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
EditText e = (EditText) v;
if(e == null)
e.setText(s);
else
e.setText(null);
Log.d(TAG, "beforeTextChanged: ");
}
Also, you can set an onClickListener
to the edittext
. This will make the edittext
null if it's not empty and clicked again.
e.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View arg0) {
if(e != null){
e.setText(null);
}
else
{}
}
});
You can also call your beforeTextChanged
method in blank else
of onClick
.
Upvotes: 0
Reputation: 2927
From the docs of beforeTextChanged
:
This method is called to notify you that, within s, the count characters beginning at start are about to be replaced by new text with length after. It is an error to attempt to make changes to s from this callback.
Hence you should move the same code to afterTextChanged
where it is legal to make changes to the EditText
.
Note: If you are setting text in afterTextChanged
on the same EditText
then the TextWatcher
will be fired. If this is not intended behaviour, should remove the TextWatcher, set the text and then add the TextWatcher back.
Upvotes: 1