看惯了可能是XXX最好的,可能是XXXX目前最好的,今天我也用下这个标题,哈哈。别喷我,当然我也就吹吹牛。有很多好的方法来实现。
本文主要还是用来讲解下InputFilter的使用。
一般金额类的输入需求比较多,我们这里就用金额输入框做实例。其他的类似的文字,大小写字母等需求限制也是同理的。
某天产品经理 A拿着菜刀到我身边说:
第一次交锋
A:小B啊,这个界面的金额输入框输入的钱小数点后最多二位,也就是最多到分,还有那个界面的这个地方,填金额也是精度到分。
唯唯诺诺的我:好的,马上完成。
1.控制小数点后位数:
因为有很多界面都要用到,所以我们专门抽出一个类来进行控制,并且我们知道,我们要控制EditText控制它的输入内容,其实相当于是对其进行过滤,所以我们让我们的类实现InputFilter
接口。
public class PointLengthFilter implements InputFilter {
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
return null;
}
}
发现一定要我们实现filter
方法,从字面意思看我们也知道是过滤,那我们来看下具体的参数字段的意思:
字段 |类型 | 内容
-------|-----------|-------------|-------------
source | CharSequence | 为即将输入的字符串
start | int | source的start, start 为0
end | int | source的end ,因为start为0,end也可理解为source长度了
dest | Spanned|输入框中原来的内容
dstart | int | 要替换或者添加的起始位置,即光标所在的位置
dend | int | 要替换或者添加的终止始位置,若为选择一串字符串进行更改,则为选中字符串 最后一个字符在dest中的位置
我们来假设下,我们通过键盘依次输入12345,我们可以看到相应的值:
source:1,start:0,end:1,dest:,dstart:0,dend:0
source:2,start:0,end:1,dest:1,dstart:1,dend:1
source:3,start:0,end:1,dest:12,dstart:2,dend:2
source:4,start:0,end:1,dest:123,dstart:3,dend:3
source:5,start:0,end:1,dest:1234,dstart:4,dend:4
大家可能会发现start一直为0,end一直为1,因为我们是依次输入的,比如你复制三个字符,通过粘贴复制的方式加入到EditText中,这时候就不是0和1了,而是0,3。
所以根据这个小数点位数需求,我们先来第一版的Filter
(有问题版本)
public class PointLengthFilter implements InputFilter {
private static final int DECIMAL_DIGITS = 2;//小数的位数
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
// 删除等特殊字符,直接返回
if ("".equals(source.toString())) {
return null;
}
//原来输入框已有的内容
String dValue = dest.toString();
//通过小数点来进行拆分,分为小数点前面的字符串和小数点后面的字符串
String[] splitArray = dValue.split("\\.");
if (splitArray.length > 1) {
//获的小数点后面的字符串
String dotValue = splitArray[1];
//判断小数点后面的当前位数是不是已经大于等于规定的2了
//如果已经2位了,则返回"";
if (dotValue.length() >= DECIMAL_DIGITS) {
return "";
}
}
return null;
}
}
然后在我们的Activity中设置:
EditText editText = (EditText) findViewById(R.id.et_money);
editText.setFilters(new InputFilter[]{new PointInputFilter()});
PS :可以把过滤的条件单独写出来分为好几个文件,因为传入的是InputFilter数组。
这样。我们终于实现了小数点后面的位数控制了。
如下图所示,我们输入12345.67之后,再输入其他字符,在filter中就默认返回了一个空的字符串"",所以就等于没输入其他内容进去。
第二次交锋:
产品经理 A拿着菜刀气冲冲的过来了。
A:你这个输入金额的有问题你知道吗?你都不能自己好好测试测试吗?
低声下气的我:不可能啊,我测试过的啊,小数点后面的数的位数不会超过2啊。
A:位数的确不超过2了,但是你修改下小数点前面的数字试试。
我拿着手机试了下,比如上面我们已经输入了12345.67,这时候我想在小数点前面的内容多加个数字,或者前面的12345我删了几个,再输入其他数字都不行了。因为小数点后的位数是一直是2,这时候我们修改小数点前的内容,就一直触发:
String[] splitArray = dValue.split("\\.");
if (splitArray.length > 1) {
//获的小数点后面的字符串
String dotValue = splitArray[1];
//判断小数点后面的当前位数是不是已经大于等于规定的2了
//如果已经2位了,则返回"";
if (dotValue.length() >= DECIMAL_DIGITS) {
return "";
}
}
所以一直返回一个空字符串,所以就无法修改小数点前面的数字了。
2.控制小数点位数的同时,更改小数点前的数字:
我们只需要改原本控制小数点的逻辑代码即可:
String dValue = dest.toString();
String[] splitArray = dValue.split("\\.");
if (splitArray.length > 1) {
String dotValue = splitArray[1];
//获取小数点“.”在字符串中的index值
int dotIndex = dValue.indexOf(".");
/添加了一个条件判断:输入光标是在小数点的后面
if (dotValue.length() >= DECIMAL_DIGITS && dstart > dotIndex) {
return "";
}
}
试了一下。果然可以自由的对小数点前面的数字随意的增删改了。哈哈。我心满意足的再次改好上交了。
第三次交锋:
产品经理这次拿着一把砍刀再次过来。
A:你这个输入金额的小数点前面的数可以输入很多,我这边考虑了下,要求小数点前面最多输入6位,加起来最大可输入的值是999999.99元。也就是不超过一百万,下班前记得完成哦。
有点气愤的我:好的。包您满意。
3.限制小数点前面的位数:
这时候其实我们也知道并不难,只要在小数点前面的位数增加控制就行:
public class PointInputFilter1 implements InputFilter {
private static final int DECIMAL_DIGITS = 2;//小数的位数
private static final int INTEGER_DIGITS = 6;//整数位的位数
private static final int TOTAL_DIGITS = 9; //整数部分 + “小数点” + 小数部分
private int currentLimitDigits = INTEGER_DIGITS;//当前控制的位数值
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
// 删除等特殊字符,直接返回
if ("".equals(source.toString())) {
return null;
}
String dValue = dest.toString();
String[] splitArray = dValue.split("\\.");
switch (splitArray.length) {
case 1:
//判断当前是否有小数点,如果有小数点就把控制位数变为TOTAL_DIGITS(其实只要比整数位数+1大就可以)
if (dValue.indexOf(".") != -1) {
currentLimitDigits = TOTAL_DIGITS;
} else {
//如果没有小数点,则继续控制前面整数位的位数为6位
currentLimitDigits = INTEGER_DIGITS;
}
/**这里如果我们直接输入999999时候,其实已经不能按其他数字了,
不然就超过一百万了,但是这时候如果输入的是小数点,则可以在输入框中显示小数点。
而且这时候在上面已经把当前的位数限制变大,
这时候就可以就可以输入其他数字,然后接下去就会跳入到下面的case 2的判断了。
**/
if(splitArray[0].length() >= currentLimitDigits && !".".equals(source.toString())){
return "";
}
break;
case 2:
String integerValue = splitArray[0];
String dotValue = splitArray[1];
int dotIndex = dValue.indexOf(".");
if (integerValue.length() >= INTEGER_DIGITS && dstart <= dotIndex) {
return "";
}
if (dotValue.length() >= DECIMAL_DIGITS && dstart > dotIndex) {
return "";
}
break;
default:
break;
}
return null;
}
}
这下终于可以交差了。然后沾沾自喜的把成果发布了一版,开心的等着下班。
第四次交锋:
产品经理推着大炮再次走了过来,
A:你的输入有问题,你看,我都输入了好几百万了。
我:不可能啊,我测试过的啊,我演示给你看,看吧。不可能输得进去的。
A:我不是键盘输入的,我是直接其他地方复制了多位数字,然后粘贴复制进去的。
我: ........
A:反正我不管,你没弄完不准下班。
我心里暗暗说了句:MMP
4.处理通过粘贴复制的方式输入
这里我们可以有二种处理方式:
- 直接就干脆不让多位数字粘贴进来。
- 针对多位数字赋值粘贴来进行处理。
<1> 不准复制粘贴多位数字:
这个很简单,如果客户是复制一位数字,然后粘贴复制进去的,其实就等效我们用键盘输入,所以就不需要特殊处理。我们只在意的是比如现在是999.99,他在前面直接粘贴了99999。就变成了99999999.99了,超过我们的范围了。我们可以直接禁止多位数字的粘贴复制,代码很简单:
//在最前面多添加一个判断,就是当输入的字符是多位的时候,直接返回空字符串。
//因为通过键盘输入我们都是一位位输入的,而多位的情况一般就是复制粘贴进来的。
if(source.length() > 1){
return "";
}
<2> 处理粘贴复制的方式的输入:
我们假设这几种情况:
(1)输入框里面的内容是整数,比如1234,然后我们复制整数9999进去,这时候应该是123499。
(2)输入框里面的内容是整数,比如1234,然后我们复制整数999.999进去,这时候应该是123499.99。
(3)输入框里面的内容是小数,比如1234.1,然后我们复制整数999进去,如果复制在小数点前面,应该是123499.1,如果复制在小数点后面,应该是1234.1。
(4)输入框里面的内容是小数,比如1234.1,然后我们复制的也是小数进去,比如9.9,我们粘贴在小数点前,则变为了123499.1,因为输入框内默认就一个小数点,复制进来的9.9我们就作为99加入到整数部分。如果加到小数点后面则变为1234.19。
(5)输入框里面是空的内容,我们输入12345678.87654321;小数点前面也超出,后面也超出,取有效部分,变为123456.87。
PS:每个人在具体的业务中可能要求不同,主要是按实际业务来,我这边是当粘贴的数字太大的时候,截取了还能放下的位数,你也可以干脆发现粘贴的数加进去后超标了。直接返回空字符串。
附上最终的代码:
PointInputFilter.java:
public class PointInputFilter implements InputFilter {
private static final int DECIMAL_DIGITS = 2;//小数的位数
private static final int INTEGER_DIGITS = 6;//整数位的位数
private static final int TOTAL_DIGITS = 9; //整数部分 + “小数点” + 小数部分
private int currentLimitDigits = INTEGER_DIGITS;
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
// 删除等特殊字符,直接返回
if ("".equals(source.toString())) {
return null;
}
/*
如果想要直接禁止复制粘贴多个数字,直接这边限制。
if(source.length() > 1){
return "";
}*/
String dValue = dest.toString();
String[] splitArray = dValue.split("\\.");
switch (splitArray.length) {
case 1:
if (dValue.indexOf(".") != -1) {
currentLimitDigits = TOTAL_DIGITS;
} else {
currentLimitDigits = INTEGER_DIGITS;
}
if (source.length() > 1) {
String sValue = source.toString();
String[] subSplitArray = sValue.split("\\.");
switch (subSplitArray.length) {
case 1:
if (source.length() + dest.length() > currentLimitDigits) {
return source.subSequence(0, currentLimitDigits - dest.length());
}
break;
case 2:
String content = "";
if (dstart == dest.length()) {
if (subSplitArray[0].length() + dest.length() > INTEGER_DIGITS) {
content += subSplitArray[0].subSequence(0, INTEGER_DIGITS - dest.length());
} else {
content += subSplitArray[0];
}
if (subSplitArray[1].length() > DECIMAL_DIGITS) {
content += "."+ subSplitArray[1].substring(0, DECIMAL_DIGITS);
} else {
content += "."+ subSplitArray[1];
}
return content;
} else {
if (subSplitArray[0].length() + dest.length() > INTEGER_DIGITS) {
content += subSplitArray[0].subSequence(0, INTEGER_DIGITS - dest.length());
} else {
content += subSplitArray[0];
}
}
return content;
default:
break;
}
}
if (splitArray[0].length() >= currentLimitDigits && !".".equals(source.toString())) {
return "";
}
break;
case 2:
String integerValue = splitArray[0];
String dotValue = splitArray[1];
int dotIndex = dValue.indexOf(".");
if (dstart <= dotIndex) {
if (integerValue.length() >= INTEGER_DIGITS) {
return "";
} else if (source.length() + integerValue.length() >= INTEGER_DIGITS) {
return source.subSequence(0, INTEGER_DIGITS - integerValue.length());
}
} else {
if (dotValue.length() >= DECIMAL_DIGITS) {
return "";
} else if (source.length() + dotValue.length() >= DECIMAL_DIGITS) {
return source.subSequence(0, DECIMAL_DIGITS - dotValue.length());
}
}
break;
default:
break;
}
return null;
}
}
总结:
完毕。终于可以安心下班了。哈哈。。。