问题描述:
在 EditText中经常要限制输入的内容,比如在输入金额的地方,我们通常需要限制它“只能输入数字和小数点”、“保留两位小数”、“金额最前面不能出现0”、“小数点不能出现在第一位”
分析问题:
限制输入一般有下面几种方法:
- 在布局文件里给EditText增加属性:android:inputType=“numberDecimal”,对应的代码设置:
//java代码
editText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL)
//Kotlin代码
editText.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
- 在布局文件里设置属性:android:digits=“123456789.”,对应的代码设置:
//Kotlin代码
class MoneyKeyListener : NumberKeyListener() {
override fun getInputType(): Int {//键盘类型
return InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
}
override fun getAcceptedChars(): CharArray {
return "123456789.".toCharArray()
}
}
- 使用DigitsKeyListener,DigitsKeyListener是继承自NumberKeyListener.java 代码示例:
//Kotlin代码
keyListener = DigitsKeyListener.getInstance(false, true)//第一个参数表示是否允许输入(+-),第二个参数表示是否允许输入小数点(.)
上面这些方法只能用于简单的限制输入,想要更加灵活的自定义的限制输入的内容,则需要用到今天的主角“InputFilter”
解决问题:
InputFilter是一个接口,用来过滤文本框中输入的内容,里面定义了一个filter方法,包含有以下几个参数:
- source: CharSequence, //将要输入的字符串,如果是删除操作则为空字符串
- start: Int, //将要输入的字符串起始下标,一般为0
- end: Int, //start + source字符的长度
- dest: Spanned, //输入之前文本框中的内容
- dstart: Int, //将会被替换的起始位置
- dend: Int //dstart+将会被替换的字符串长度
- 返回值 :CharSequence //方法返回的值将会替换掉dest字符串中dstartd位置到dend位置之间字符,返回source表示不做任何处理,返回空字符串""表示不输入任何字符
为了便于理解这些参数的意义打印出了一些Log:
- 连续输入1、2、3、4、5、6,输入框显示:123456,日志如下:
D/MoneyInputFilter: [source:1][start:0][end:1][dest:][dstart:0][dend:0]
D/MoneyInputFilter: [source:2][start:0][end:1][dest:1][dstart:1][dend:1]
D/MoneyInputFilter: [source:3][start:0][end:1][dest:12][dstart:2][dend:2]
D/MoneyInputFilter: [source:4][start:0][end:1][dest:123][dstart:3][dend:3]
D/MoneyInputFilter: [source:5][start:0][end:1][dest:1234][dstart:4][dend:4]
D/MoneyInputFilter: [source:6][start:0][end:1][dest:12345][dstart:5][dend:5]
- 删除6 ,输入框显示:12345,日志如下:
D/MoneyInputFilter: [source:][start:0][end:0][dest:123456][dstart:5][dend:6]
- 光标定位到3后面,删除3,输入框显示:1245,日志如下:
D/MoneyInputFilter: [source:][start:0][end:0][dest:12345][dstart:2][dend:3]
- 选中2和4,输入3,输入框显示:135,日志如下:
D/MoneyInputFilter: [source:3][start:0][end:1][dest:1245][dstart:1][dend:3]
- 选中3,从其他地方粘贴"234",输入框显示:12345,日志如下:
D/MoneyInputFilter: [source:234][start:0][end:3][dest:135][dstart:1][dend:2]
理解了各个参数意义就可以根据项目需要增加各种输入限制
完整金额过滤器代码(Kotlin):
/**
* 金额输入过滤器
* 1、不允许第一个字符为"."
* 2、不允许出现0123、0456这类字符串
* 3、保留两位小数
*
* @author zhujie
* @date 2019/6/24
* @time 15:40
*/
class MoneyInputFilter : InputFilter {
companion object {
val POINT_LENGTH = 2//保留小数点位数
}
override fun filter(
source: CharSequence,//将要输入的字符串,如果是删除操作则为空
start: Int,//将要输入的字符串起始下标,一般为0
end: Int,//start + source字符的长度
dest: Spanned,//输入之前文本框中的内容
dstart: Int,//将会被替换的起始位置
dend: Int//dstart+将会被替换的字符串长度
): CharSequence {//方法返回的值将会替换掉dest字符串中dstartd位置到dend位置之间字符,返回source表示不做任何处理,返回空字符串""表示不输入任何字符
Log.d("MoneyInputFilter", "[source:$source][start:$start][end:$end][dest:$dest][dstart:$dstart][dend:$dend]")
val start = dest.subSequence(0, dstart)
val end = dest.subSequence(dend, dest.length)
val target = start.toString() + source + end//字符串变化后的结果
val backup = dest.subSequence(dstart, dend)//将要被替换的字符串
if (target.indexOf(".") == 0) {//不允许第一个字符为.
return backup
}
if (target.startsWith("0") && !target.startsWith("0.") && "0" != target) {//不允许出现0123、0456这类字符串
return backup
}
//限制小数点后面只能有两位小数
val index = target.indexOf(".")
if (index >= 0 && index + POINT_LENGTH + 2 <= target.length) {
return backup
}
return source
}
}
使用方法:
//java
editText.setFilters(new InputFilter[]{new MoneyInputFilter ()});
//Kotlin
editText.filters = arrayOf(MoneyInputFilter())