SAKA'S BLOG

EditText的InputFilter

在安卓中我们会经常使用EditText,这个控件允许我们输入字符并获取字符.有时我们需要对输入的字符内容进行过滤,通常情况下是自定义正则表达式,这样定制性更高.但是本节将要讲的是哈斯用InputFilter来过滤输入的文本,同样可以达到正则表达式的部分功能.

InputFilter有两个直接子类,AllCaps和LengthFilter.

InputFilter.AllCaps

这个过滤器将会将输入的所有的英文小写字母变为大写字母.

InputFilter.LengthFilter

这个过滤器会设置一个输入的最大长度,当输入的长度超过设置的最大值时,只显示前边的字符.

该过滤器的作用相当于XML布局文件的maxLength属性.
当你需要在java代码中使用固定长度的方法时你会发现没有setMaxLength方法,我们需要定义一个过滤器数组,然后添加LengthFilter到数组,并赋给给EditText才会产生一个拥有固定长度的EditText.

1
2
private InputFilter[] filters={new InputFilter.LengthFilter(5)};
editText.setFilters(filters);

这两个类都非常简单,它们主要是利用了InputFilter接口的中的filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend)方法来实现对字符的操作.
那么我们可以通过模拟上边两个类来实现我们自己的过滤器,例如:

  1. 将所有的大写字母转换为小写字母

首先看一下接口文档的方法说明:

1
2
3
4
5
6
CharSequence filter (CharSequence source, 
int start,
int end,
Spanned dest,
int dstart,
int dend)

当缓冲区的字符要使用从source字符串的start到end位置的字符替换dest的字符串的从dstart到dend的字符时会调用这个方法,其实就是输入或者删除字符串的时候.返回的是你替换好的字符串,包括空字符串(如果对此适用),或者null来接受替换操作.注意,当删除字符串时不要忽略对0长度的字符串的替换.同样需要注意的是不要试图更改dest的内容,你只能在上下文中检查dest.如果soruce是Spanned或者Spannable的实例,source应该被复制到过滤后的结果中,使用copySpansFrom(Spanned, int, int, Class, Spannable, int)方法会更简单.

画一个简单的模型:

我们数据流从键盘接收,然后经过一个缓冲区,最后由缓冲区进入EditText显示.

加上filter以后相当于加上了一个拦截器,此时模型变为:

输入的字符要经过filter转换以后才显示到EditText上.

source相当于缓冲区给filter的字符,dest相当于未更改前的EditText上显示的字符串,当经过我们的filter后会立即转变为输入后filter转换的字符串.

理解了这个原理后我们就可以定义自己的过滤器了.

实现大写转小写的过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ublic class Alllower implements InputFilter {
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
Log.d("inputfilter", "source=" + source + ",start=" + start + ",end=" + end +
",dest=" + dest + ",dstart=" + dstart + ",dend=" + dend);
for (int i = start; i < end; i++) {
if (Character.isUpperCase(source.charAt(i))) {//是否大写的字符
char[] v = new char[end - start];//是大写的字符则新建一个等长的数组
TextUtils.getChars(source, start, end, v, 0);//将source中的字符复制到我们新建的数组v中
String s = new String(v).toLowerCase();//将我们新建的数组中的字符全部转换为小写
if (source instanceof Spanned) {//如果是Spanble的情况下要保持原样返回
SpannableString sp = new SpannableString(s);
TextUtils.copySpansFrom((Spanned) source, start, end, null, sp, 0);
Log.d("inputfiler", "sp=" + sp);
return sp;
} else {
Log.d("inputfilter", "s=" + s);
return s;
}
}
}
return null;//未发生转换或者删除字符的时候我们要保持不变
}
}

此时我们可以测试一下这个过滤器是否有用:

1
private InputFilter[] filters={new Alllower(),new InputFilter.LengthFilter(5)};editText.setFilters(filters);

看一下结果:

可以看到只是将大写字母转换为了小写字母,并没有影响其他.

其他实现接口的类

DateKeyListener,DateTimeKeyListener,DialerKeyListener, DigitsKeyListener,TimeKeyListener均继承自NumberKeyListener,而NumberKeyListener实现了InputFilter接口.但是这些都是用来监听硬件输入设备的,暂不研究.
LoginFilter, LoginFilter.PasswordFilterGMail, LoginFilter.UsernameFilterGMail, LoginFilter.UsernameFilterGeneric是实现的一个专门为Gmail登录的过滤器.