I'm trying to implement an EditText that limits input to alpha chars only [A-Za-z].
I started with the InputFilter method from this post. When I type "a%" the text disappears then if I hit backspace the text is "a". I've tried other variations on the filter function like using a regex to match only [A-Za-z] and sometimes see crazy behavior like repeating chars, I'll type "a" then "b" and get "aab" then type "c" and get "aabaabc" then hit backspace and get "aabaabcaabaabc"!
Here's the code I'm working with so far with the different approaches I've tried.
EditText input = (EditText)findViewById( R.id.inputText );
InputFilter filter = new InputFilter() {
@Override
public CharSequence filter( CharSequence source, int start, int end, Spanned dest, int dstart, int dend ) {
//String data = source.toString();
//String ret = null;
/*
boolean isValid = data.matches( "[A-Za-z]" );
if( isValid ) {
ret = null;
}
else {
ret = data.replaceAll( "[@#$%^&*]", "" );
}
*/
/*
dest = new SpannableStringBuilder();
ret = data.replaceAll( "[@#$%^&*]", "" );
return ret;
*/
for( int i = start; i < end; i++ ) {
if( !Character.isLetter( source.charAt( i ) ) ) {
return "";
}
}
return null;
}
};
input.setFilters( new InputFilter[]{ filter } );
I'm totally stumped on this one so any help here would be greatly appreciated.
EDIT: Ok, I've done quite a lot of experimenting with InputFilter and have drawn some conclusions, albeit no solution to the problem. See the comments in my code below. I'm going to try Imran Rana's solution now.
EditText input = (EditText)findViewById( R.id.inputText );
InputFilter filter = new InputFilter() {
// It is not clear what this function should return!
// Docs say return null to allow the new char(s) and return "" to disallow
// but the behavior when returning "" is inconsistent.
//
// The source parameter is a SpannableStringBuilder if 1 char is entered but it
// equals the whole string from the EditText.
// If more than one char is entered (as is the case with some keyboards that auto insert
// a space after certain chars) then the source param is a CharSequence and equals only
// the new chars.
@Override
public CharSequence filter( CharSequence source, int start, int end, Spanned dest, int dstart, int dend ) {
String data = source.toString().substring( start, end );
String retData = null;
boolean isValid = data.matches( "[A-Za-z]+" );
if( !isValid ) {
if( source instanceof SpannableStringBuilder ) {
// This works until the next char is evaluated then you get repeats
// (Enter "a" then "^" gives "a". Then enter "b" gives "aab")
retData = data.replaceAll( "[@#$%^&*']", "" );
// If I instead always returns an empty string here then the EditText is blanked.
// (Enter "a" then "^" gives "")
//retData = "";
}
else { // source is instanceof CharSequence
// We only get here if more than 1 char was entered (like "& ").
// And again, this works until the next char is evaluated then you get repeats
// (Enter "a" then "& " gives "a". Then enter "b" gives "aab")
retData = "";
}
}
return retData;
}
};
input.setFilters( new InputFilter[]{ filter } );
Use the following code:
If you want the valid text to remain in the EditText:
Bingo, I found the problem!
When I use android:cursorVisible="false" on the EditText the start and dstart parameters don't match up correctly.
The start parameter is still always 0 for me, but the dstart parameter is also always 0 so it works out as long as I use .replaceAll(). This is contrary to what this post says so I don't quite understand why but at least I can build something that works now!
I would just like to add my solution to the problem(as late as it is). I found that if you add
Then the backspace problems stop
We had a similar problem and I believe a solution[0] that would work for you as well. Our requirements were to implement an EditText that stripped rich text input. For example, if the user copied bold text to their clipboard and pasted it into the EditText, the EditText should remove the bold emphasis styling and preserve only the plain text.
The solution class looks something like this:
Our original implementation for stripRichText() was simple:
The Java base String class doesn't retain any styling information so converting the CharSequence interface to a concrete String copies only plain text.
What we didn't realize was that some Android soft keyboards add and depend on temporary compositional hints for typos and other things. The problem manifests by removing the hints as well as repeating characters in an unexpected way (usually doubling the entire EditText field's input). The documentation[1] for InputFilter.filter() communicates the requirement this way:
I believe the proper solution is to preserve temporary spans:
[0] Actual implementation available here: https://git.wikimedia.org/blob/apps%2Fandroid%2Fwikipedia/e9ffffd8854ff15cde791a2e6fb7754a5450d6f7cf/app%2Fsrc%2Fmain%2Fjava%2Forg%2Fwikipedia%2Frichtext%2FRichTextUtil.java
[1] https://android.googlesource.com/platform/frameworks/base/+/029942f77d05ed3d20256403652b220c83dad6e1/core/java/android/text/InputFilter.java#37
Fix for repeating text, work on all Android Versions:
Now you can use this filer like: