Android InputFilter详解
项目中遇到一个需求,需要限制EditText只能输入到小数点后两位网上有两种方案,一种是使用TextWatcher,另一种就是使用InputFilter,感觉使用InputFilter的方式比较优雅,比如EditText android:inputType限制各种输入类型就是通过各种各种InputType来实现的。还有maxLength限制EditText输入长度也是通过InputFilter实现
项目中遇到一个需求,需要限制EditText只能输入到小数点后两位
网上有两种方案,一种是使用TextWatcher,另一种就是使用InputFilter,感觉使用InputFilter的方式比较优雅,比如EditText android:inputType限制各种输入类型就是通过各种各种InputType来实现的。还有maxLength限制EditText输入长度也是通过InputFilter实现的
源码
先来看下InputFilter的源码
就是一个简单的接口,里面还有两个默认的实现类AllCaps和LengthFilter,其中LengthFilter就是限制EditText输入内容最大长度的实现类,很简单,也很具有参考价值,感兴趣的同学可以研究下
package android.text;
import android.annotation.NonNull;
import com.android.internal.util.Preconditions;
import java.util.Locale;
/**
* InputFilters can be attached to {@link Editable}s to constrain the
* changes that can be made to them.
*/
public interface InputFilter
{
/**
* This method is called when the buffer is going to replace the
* range <code>dstart … dend</code> of <code>dest</code>
* with the new text from the range <code>start … end</code>
* of <code>source</code>. Return the CharSequence that you would
* like to have placed there instead, including an empty string
* if appropriate, or <code>null</code> 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 <code>dest</code>
* from this method; you may only examine it for context.
*
* Note: If <var>source</var> is an instance of {@link Spanned} or
* {@link Spannable}, the span objects in the <var>source</var> should be
* copied into the filtered result (i.e. the non-null return value).
* {@link TextUtils#copySpansFrom} can be used for convenience if the
* span boundary indices would be remaining identical relative to the source.
*/
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend);
/**
* This filter will capitalize all the lowercase and titlecase letters that are added
* through edits. (Note that if there are no lowercase or titlecase letters in the input, the
* text would not be transformed, even if the result of capitalization of the string is
* different from the string.)
*/
public static class AllCaps implements InputFilter {
// 省略好多代码
}
/**
* This filter will constrain edits not to make the length of the text
* greater than the specified length.
*/
public static class LengthFilter implements InputFilter {
private final int mMax;
public LengthFilter(int max) {
mMax = max;
}
public CharSequence filter(CharSequence source, int start, int end, Spanned dest,
int dstart, int dend) {
int keep = mMax - (dest.length() - (dend - dstart));
if (keep <= 0) {
return "";
} else if (keep >= end - start) {
return null; // keep original
} else {
keep += start;
if (Character.isHighSurrogate(source.charAt(keep - 1))) {
--keep;
if (keep == start) {
return "";
}
}
return source.subSequence(start, keep);
}
}
/**
* @return the maximum length enforced by this input filter
*/
public int getMax() {
return mMax;
}
}
}
源码分析
看完InputFilter的接口定义是很简单,但是到底怎么使用呢,filter()方法里的六个参数又分别表示什么呢?初看源码里的英文注释觉得懵懵的,但是如果理解了再返回看其实注释里讲得挺清晰的。网上很多文章对这些参数的说明都是一笔带过,讲得不明不白的。但是如果你想使用InputFilter就必须把这几个参数分别表示什么都搞明白。
source:新输入的内容
start:新输入内容起始位置
end:新输入内容结束位置
dest:原来的内容
dstart:待输入(插入)位置光标的起点
dend:待输入(插入)位置光标的终点
这样写相信大家还是看不明白,下面会对每个参数再做说明
我们可以先写一个最简单的InputFilter,然后打印看看各个参数的值
public class MyInputFilter implements InputFilter {
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
Log.d("InputFilter", source.toString() + " " + start + " " + end
+ " " + dest.toString() + " " + dstart + " " + dend);
// 返回null表示不做任何过滤转换
return null;
}
}
mInput.setFilters(new InputFilter[]{new MyInputFilter()});
假设我们有一个EditText,内容为野原新一,输入的光标停留在内容最后,然后我们新输入了A这个字母,但A还没最终在EditText上显示出来,设置InputFilter的作用就是对新输入的内容做过滤变换等各种操作,在我们输入A后还未在EditText中显示出来前,会先走到InputFilter的的filter方法,此时各个参数的值如下
source:A
start:0
end:1
dest:野原新一
dstart:4
dend:4
日志打印如下,如我所预料的
A 0 1 野猿新一 4 4
看完上面最简单的示例后是不是就比较好理解了,下面再对每个参数做更详细的分析
参数source
参数source就是新输入但是还没生效的内容,在生效前可以通过InputFilter来做各种过滤和转换
参数source在上面简单的例子为一个字符A,长度为1,正常的软键盘输入字母或者数字的情况下每次的source都是一个字符,但是对于比如汉字的拼音输入方,有可能source为多个字符,比如用拼音yuyuan输入野原这两个汉字,这时source长度是2
source长度大于2还有另一种情况就是复制黏贴的情况,比如我们复制了野原新二四个字黏贴到EditText后面,此时的source就变成野原新二四个字,长度为4,此时的start为0,end为4
end正常情况下就是等于source的长度
参数start
正常情况下都是0,不正常情况我也不几道
参数end
正常情况下都是等于source.length(),不正常情况我也布吉岛
参数dest
参数dest就是原来新输入内容还没生效前的原来的内容
参数dstart
参数dstart正常情况下,也就是光标在输入框内容最后面的情况下,dstart的值为dest.length(),也就是原来内容的长度
不正常情况下就是输入内容不是在尾部,而是从中间插入,或者插入到前面,这时候dstart的值就是光标的位置,这里说的光标位置是指起始的光标位置,结束光标的位置在参数dend的说明时会说到
参数dend
参数dend正常情况下的值等于dstart的值,也就是光标的位置,这种情况下是只考虑输入内容为插入的情况
还有一种情况是替换内容,这时候光标是有一个范围的,分为起始光标dstart和结束光标,起始光标和结束光标间选中的内容,在新输入内容生效后会被新的内容替换
如下图所示的内容,此时的dstart=2,dend=6,这样就很好理解了,dstart等于替换内容第一个字符的位置,dend等于替换内容最后一个字符的后一个位置,也就是dend的值是exclude不包含的
删除的情况
前面考虑的都是输入内容的情况,删除(或者剪切)内容的情况也是会调用InputFilter的
比如EditText原来的内容为‘野原新一’,当我们删除最后一个字符‘一’的时候,filter()方法打印的各个参数内容如下
0 0 野猿新一 3 4
source:
start:0
end:0
dest:野原新一
dstart:3
dend:4
直接看打印的参数值就很清晰明了,因为是删除,所以source是空的,dstart就是删除的最后一个字符的位置
返回值
对于InputFilter除了要理解各个参数的含义外,还需要知道不同的返回值的不同含义
返回null:不做任何过滤和转换,输入什么就是什么
返回"":将source替换为空,也就是不允许任何输入
返回具体的内容:将source替换为具体的内容
最简单的实例
下面我们写几个最简单的示例来验证我们上面的结论
下面例子非常简单,filter()方法直接返回"",也就是任何输入都替换成空,这个例子可以用来禁止EditText输入任何的内容
public class InputFilter1 implements InputFilter {
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
return "";
}
}
这个例子会把任何输入的内容都替换成‘野原新一’这四个字,比如你在键盘输入'A',在EditText中会显示‘野原新一’
public class InputFilter1 implements InputFilter {
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
return "野原新一";
}
}
只要理解了InputFilter的filter()方法中各个参数的含义及retuan的使用方法,不管任何需求,任何复杂的过滤和变换都可以实现。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)