Android InputFilter详解
创始人
2025-05-29 16:53:41
0

项目中遇到一个需求,需要限制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 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 {@link Spanned} or* {@link Spannable}, the span objects in the source 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 {@Overridepublic 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 {@Overridepublic CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {return "";}
}

这个例子会把任何输入的内容都替换成‘野原新一’这四个字,比如你在键盘输入'A',在EditText中会显示‘野原新一’

public class InputFilter1 implements InputFilter {@Overridepublic CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {return "野原新一";}
}

只要理解了InputFilter的filter()方法中各个参数的含义及retuan的使用方法,不管任何需求,任何复杂的过滤和变换都可以实现。

相关内容

热门资讯

银行、消金公司助贷余额增速不得... 近日,中国证券报记者从多位业内人士处独家获悉,5月以来,多地金融监管部门对部分中小银行、消金公司下达...
朱鸿接任陈航,担任钉钉科技有限... 消费日报-今朝新闻讯 天眼查显示,6月23日,钉钉科技有限公司发生工商变更,陈航卸任法定代表人、董事...
3日累跌超20%,德创环保:公... 6月25日, 德创环保(603177.SH)公告,公司股票于2026年6月23日、6月24日和6月2...
北京发布2026年第七轮拟供商... 央广网北京6月25日消息(记者门庭婷)6月25日,北京市规划和自然资源委员会网站发布了2026年第七...
开放麦 | 启明创投胡奇:从A... “2026年,创投圈的浪潮再次翻涌:AI从技术概念走进产业深水区,硬科技创业从“小众赛道” 变成“主...
腾讯孙忠怀:在行业转身处 6月24日,2026腾讯视频年度发布在上海举行。腾讯公司副总裁、腾讯在线视频董事长孙忠怀以《在行业转...
加息,突变!美联储,重磅传来!... 美联储政策路径突生变数。 美国商务部经济分析局最新公布的数据显示,5月个人消费支出(PCE)物价指数...
6月合肥上门收金必看!5步避坑... 2026年6月,合肥黄金市场持续高位运行,不少市民翻出家里闲置的旧金饰、投资金条想变现,上门回收因为...
潮汕女富豪挂帅后加码液冷!祥鑫... 潮汕女强人,带着百亿公司加码液冷散热。 6月24日晚间,祥鑫科技(002965.SZ)公告称,公司董...
马斯克向太空要电,GobiX ... 一场关于「去哪里找电」的全球竞赛,正在朝两个方向展开。 作者|周永亮 编辑| 郑玄 「太空光伏是不是...
原料药行业陷入周期低谷 有药企... 每经记者|许立波 每经编辑|魏文艺 “过完年到现在,我们整个团队每个月都在出差,跑遍了亚非拉、欧美市...
家门口筛查白内障!永顺泽家镇暖... 大众卫生报·新湖南客户端6月25日讯(通讯员 彭雪姣)为切实解决辖区老年性白内障患者异地就医奔波、就...
终于等到!油价马上再大跌,这个... 点击添加图片描述(最多60个字) 编辑 各位车主朋友,好消息接二连三! 继6月18日油价大幅下调...
丈量出海新路 世界酒庄影响力指... 长期以来,全球酒庄评价体系由西方机构主导,且大多局限于单一酒种、单一评价维度,这一局面正逐渐被打破。...
峰瑞资本创始合伙人李丰:从资本... “2026年,创投圈的浪潮再次翻涌:AI从技术概念走进产业深水区,硬科技创业从“小众赛道” 变成“主...
原创 A... 迈向成熟,还有茁壮成长的机会。 作者 | 方璐 编辑丨于婞 来源 | 野马财经 2026年6月21日...
为企业解锁出海新通道!亚太中小... 6月24日下午,作为2026年APEC中小企业工商论坛的重要组成部分,亚太中小企业国际化合作发展论坛...
君赛生物港股IPO,增聘兴证国... 跟丰宜科技一样,正冲刺港股IPO的上海君赛生物股份有限公司(简称“君赛生物”)增聘一位整体协调人。 ...
圣邦股份明日上市:暗盘涨24%... 雷递网 雷建平 6月25日 圣邦微电子(北京)股份有限公司(简称:“圣邦股份”,股票代码:“0366...
科技“吃肉”,券商跟着“喝汤”... 当科技持续成为市场核心主线,押中硬科技项目的券商也成为被追逐的焦点。 6月24日,半导体零部件概念股...