金额输入控件
最近在做一个金融的项目,里面有关于金额的判断和限制金额的输入,然后就发现了些问题。比如我们输入金额的时候肯定只能输入数字和点号,不能输入其他的字符;还有进行金额判断的时候很麻烦,比如要金额不能为0878787.98、.898或者0000000.98等等这样的。每次都要进行这样的判断很麻烦,又很耗时而且又容易出错,然后就在想能不能自己自定义一个控件来限制输入规范它,并且能将判断在内部处理。
首先我们要控制输入类型,这个很简单,我们只需要设置EditText的inputType类型为numberDecimal就行了。然后我们需要控制输入内容了,比如我们第一位输入的是为0,那么下一位必须为点号,不能继续输入数字,那么我们怎么实现这样的功能呢?
然后我们去看EditText的源码,发现没有。
然后我们发现EditText继承TextView,那么我们去看下TextView的源码
我们在这个发现了setFilters这个方法,感觉是,我们继续看下去
纯英文的,看不懂,没关系,我们百度看这个是不是我们需要找的
TextView和EditText中的setFilters方法说明
看来这个确实是我们要找的(在这篇文章中写的很清楚了,这里就不再说了),那么我们怎样设置规则呢?
好了,现在我们要开始设置规则了,那我们先考虑下,我们需要设置哪些规则?
- 如果以0开头
- 则下一位必须是点
- 0和点之间不能输入任何数字
- 如果不是0开头
- 不能将0放在字符串的第一位
- 第一位不能输入“.”
- 如果第一位为“.”,则点号后面不能继续输入
- 保留两位小数
- 验证删除等按键
将规则写清楚之后,我们就直接上代码吧
public class AmountFilter implements InputFilter {
private final String TAG = "AmountFilter";
Pattern mPattern;//正则匹配
private static final int POINTER_AFTER_LENGTH = 2; //小数点后的位数
private static final String POINTER = ".";
private static final String ZERO = "0";
public AmountFilter() {
//正则表达式
mPattern = Pattern.compile("([0-9]|\\.)*");
}
/**
* @param source 新输入的字符串
* @param start 新输入的字符串起始下标,一般为0
* @param end 新输入的字符串终点下标,一般为source长度-1
* @param dest 输入之前文本框内容
* @param dstart 原内容起始坐标,一般为0
* @param dend 原内容终点坐标,一般为dest长度-1
* @return 输入内容
*/
@Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
String sourceText = source.toString();
String destText = dest.toString();
//验证删除等按键
if (TextUtils.isEmpty(sourceText)) {
return "";
}
//如果是0开头,则下一位必须是点
if (destText.startsWith(ZERO)){
if (!TextUtils.equals(POINTER,source) && destText.length() == 1){
sourceText = "";
}
//0和点之间不能输入任何数字
else if (destText.contains(POINTER)){
int point = destText.indexOf(POINTER);
if (dstart <= point && dstart > 0){
sourceText = "";
}
}
else if (!destText.contains(POINTER)
&& dstart != 0 && destText.length() != 1){
sourceText = "";
}
}
//不能将0放在字符串的第一位
if (!TextUtils.isEmpty(destText) && dstart == 0
&& TextUtils.equals(ZERO,sourceText)
&& !destText.startsWith(POINTER)){
sourceText = "";
}
//第一位不能输入“.”
if (TextUtils.isEmpty(destText) && TextUtils.equals(sourceText,POINTER)){
sourceText = "";
}
//如果第一位为“.”,则点号后面不能继续输入
if (destText.startsWith(POINTER) && dstart != 0){
sourceText = "";
}
//保留POINTER_AFTER_LENGTH位小数
if (!destText.contains(POINTER) && TextUtils.equals(POINTER,sourceText)){
int decimal = destText.length() - dstart;
if (decimal > 2){
sourceText = "";
}
}else if (destText.contains(POINTER)){
int point = destText.indexOf(POINTER);
if (dstart > point && (destText.length() - point) >= 3){
sourceText = "";
}
}
String result = dest.subSequence(dstart, dend) + sourceText;
return result;
}
}
那么我们自定义一个AmountEditText
SuppressLint("AppCompatCustomView")
public class AmountEditText extends EditText {
private Context mContext;
public AmountEditText(Context context) {
super(context);
init(context);
}
public AmountEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public AmountEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context){
mContext = context;
}
@Override
public void setFilters(InputFilter[] filters) {
filters = new InputFilter[]{new AmountFilter()};
super.setFilters(filters);
}
}
那么我们运行下看效果吧
可以看到只能输入两位小数,然后小数位就不能输入了。如果第一位输入为0,呢后以为只能输入点,输入其他的数字无法输入。那么到这里是不是已经完成了呢?不知道大家发现问题没有,有没有可能出现02342这种情况呢,理论上是不会出现的,我们现在试下
从图中可以看到是可能出现这种情况的,那么为什么能出现这种情况呢?这是因为InputFilter这能限制输入情况,已经存在的内容是没有办法控制的,那么我们就没有办法了吗?
当然这种情况也是有办法的。上面是对输入内容的限制,那么我们只要对输入结果做一个判断,检验输入结果是否符合规范就行了。
/**
* 输入内容是否符合规则
* @return
*/
public boolean isConformRules(){
String result = super.getText().toString();
if (TextUtils.isEmpty(result)){
return false;
}else if (result.contains(".")){
if (result.startsWith(".")){
return false;
}else if (result.startsWith("0")){
int indexZero = result.indexOf("0");
int indexPoint = result.indexOf(".");
if (indexPoint - indexZero != 1){
return false;
}else if (TextUtils.equals("0.",result)){
return false;
}
}
}else if (!result.contains(".")){
if (result.startsWith("0")){
return false;
}
}
return true;
}
当我们输入完成之后,只需调用这个方法进行判断就行了。如果符合规则就返回true,不符合规则为false.那我们来检验一下
从图片中可以看到,已经完全ok的。最后,有时我们需要将金额转为分,即将结果*100,这里我们也设置了
/**
* 获取设置倍数
* @return
*/
public int getMultiple() {
return Multiple;
}
/**
* 设置金额倍数
* @param multiple
*/
public void setMultiple(int multiple) {
Multiple = multiple;
}
只需在这里设置倍数即可,当然默认倍数为1,即原值不变
最后将Demo上传,需要的同学可以下载,有问题的话也可以在下方留言,谢谢!