Sometimes you need to filter out input in text fields. Wouldn’t it be nice to be able to have a reusable class for this? Dream no longer! Today I’ll show you how you can create your own Regex InputFilter.
First, let’s take a look at the InputFilter class reference. Note that this is an interface and allows us to easily plug into our android app.
This interface has one method, called filter
. As per the documentation:
public abstract CharSequence filter (CharSequence source, int start, int end, Spanned dest, int dstart, int dend)
Added in API level 1
This method is called when the buffer is going to replace the range dstart … dend of dest with the new text from the range start … end of source. Return the CharSequence that you would like to have placed there instead, including an empty string if appropriate, or null to accept the original replacement. Be careful to not to reject 0-length replacements, as this is what happens when you delete text. Also beware that you should not attempt to make any changes to dest from this method; you may only examine it for context. Note: If source is an instance of Spanned or Spannable, the span objects in the source should be copied into the filtered result (i.e. the non-null return value). copySpansFrom(Spanned, int, int, Class, Spannable, int) can be used for convenience.
In short, return an empty string (""
) when we want to block input on a match
and return null
for any situation where we want to allow our input.
So all we need to do is create our own class that implements InputFilter and uses a Regex to determine whether or not we should allow any further input from the keyboard.
Create a new class named RegexInputFilter
and implement InputFilter:
public class RegexInputFilter implements InputFilter {
}
This class requires our interface method, so add it:
@Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
}
Create a constructor… What do we need to apply a regex? Well… A Pattern
object would do. Add a couple of private String fields for the regex Pattern
and class name.
private Pattern mPattern;
private static final String CLASS_NAME = RegexInputFilter.class.getSimpleName();
public RegexInputFilter(Pattern pattern) {
if (pattern == null) {
throw new IllegalArgumentException(CLASS_NAME + " requires a regex.");
}
mPattern = pattern;
}
Awesome. Wouldn’t it be helpful to have a constructor that takes a String also so that we don’t have to create a new Pattern object every time? I think that sounds fair:
/**
* Convenience constructor, builds Pattern object from a String
* @param pattern Regex string to build pattern from.
*/
public RegexInputFilter(String pattern) {
this(Pattern.compile(pattern));
}
Now for the part you’ve been waiting for. How do we filter?
@Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
Matcher matcher = mPattern.matcher(source);
if (!matcher.matches()) {
return "";
}
return null;
}
Was that easier than you expected? In my use case the filter rejects any input
that does not match the regex you add in, so if you wanted to allow only
letters you could pass [A-Z-a-z]
as an argument.
Then to use it, in your Activity class you can attach it to a text field using the following (for example):
textField.setFilters(new InputFilter[] {
new RegexInputFilter("[A-Za-z]")
});
Or even combine it with others:
textField.setFilters(new InputFilter[] {
new RegexInputFilter("[A-Za-z]"),
new InputFilter.LengthFilter(10)
});
Here’s the full class code, in case you biffed:
public class RegexInputFilter implements InputFilter {
private Pattern mPattern;
private static final String CLASS_NAME = RegexInputFilter.class.getSimpleName();
/**
* Convenience constructor, builds Pattern object from a String
* @param pattern Regex string to build pattern from.
*/
public RegexInputFilter(String pattern) {
this(Pattern.compile(pattern));
}
public RegexInputFilter(Pattern pattern) {
if (pattern == null) {
throw new IllegalArgumentException(CLASS_NAME + " requires a regex.");
}
mPattern = pattern;
}
@Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
Matcher matcher = mPattern.matcher(source);
if (!matcher.matches()) {
return "";
}
return null;
}
}
I’d also suggest subclassing and building the Regex into the subclass if there’s a particular regex you find yourself using multiple times. It will make your life a lot easier during refactoring. That might look something like this:
public class SpecialCharacterInputFilter extends RegexInputFilter {
private static final String SPECIAL_CHARACTER_REGEX = "[ a-zA-Z0-9]+";
public SpecialCharacterInputFilter() {
super(SPECIAL_CHARACTER_REGEX);
}
}