android中卡号输入框控件(每四位用空格分隔)(解决输入法跳转的问题)

来源:互联网 发布:手机sd卡数据免费恢复 编辑:程序博客网 时间:2024/06/03 17:56

转载:android中卡号输入框控件(每四位用空格分隔)(解决输入法跳转的问题)

感谢作者,感觉非常好用!如需转载,请注明原创作者出处!

由于项目的需求,需要在卡号输入时,每四位用空间分隔,于是就写了个控件。

该控件支持中间删除,中间增加,粘贴,末尾输入等,光标的位置显示正确。

主要的思想就是:对于添加TextWatcher监听Text的改变,text改变后,拿到该text,将text中的所有空格去掉。然后重新排列。记下来是对光标的位置处理。

  1. 在末尾删除或者增加的时候,光标一直处于末端。
  2. 在中间删除的时候,检测光标是否在空格的后面,如果在后面,就让光标前进(往左)一位,如果不是,则光标的位置不变(根据start,count,before这几个参数来算)。
  3. 在中间插入的时候,检测光标是否在空格的前面,如果在前面,则让光标前进(往右)一位,如果不是,则光标的位置不变(根据start,count,before这几个参数来算)。
    读者要先懂得start,count,before的意义。

直接上代码

/** * Created by hzlinxuanxuan on 2015/12/10. */public class CardInputEditText extends CleanUpEditText {    public CardInputEditText(Context ctx) {        this(ctx, null);    }    public CardInputEditText(Context context, AttributeSet attrs) {        super(context, attrs);        addTextChangedListener(watcher);    }    public CardInputEditText(Context context, AttributeSet attrs, int defStyleAttr){        super(context, attrs,defStyleAttr);        addTextChangedListener(watcher);    }    private TextWatcher watcher = new TextWatcher() {        @Override        public void beforeTextChanged(CharSequence s, int start, int count, int after) {        }        @Override        public void onTextChanged(CharSequence s, int start, int before, int count) {            LogUtil.d("s:" + s + ",start:" + start + ",before:" + before + ",count:" + count);            if (s == null) {                return;            }            //判断是否是在中间输入,需要重新计算            boolean isMiddle = (start + count) < (s.length());            //在末尾输入时,是否需要加入空格            boolean isNeedSpace = false;            if (!isMiddle && s.length() > 0 && s.length() % 5 == 0) {                isNeedSpace = true;            }            if (isMiddle || isNeedSpace) {                String newStr = s.toString();                newStr = newStr.replace(" ", "");                StringBuilder sb = new StringBuilder();                for (int i = 0; i < newStr.length(); i += 4) {                    if (i > 0) {                        sb.append(" ");                    }                    if (i + 4 <= newStr.length()) {                        sb.append(newStr.substring(i, i + 4));                    } else {                        sb.append(newStr.substring(i, newStr.length()));                    }                }                removeTextChangedListener(watcher);                setText(sb);                //如果是在末尾的话,或者加入的字符个数大于零的话(输入或者粘贴)                if(!isMiddle || count > 1){                    setSelection(sb.length());                } else if (isMiddle) {                    //如果是删除                    if (count == 0) {                        //如果删除时,光标停留在空格的前面,光标则要往前移一位                        if ((start - before + 1) % 5 == 0) {                            setSelection((start - before) > 0 ? start - before : 0);                        } else {                            setSelection((start - before + 1) > sb.length() ? sb.length() : (start - before + 1));                        }                    }                    //如果是增加                    else {                        if ((start - before + count) % 5 == 0) {                            setSelection((start + count - before + 1) < sb.length() ? (start + count - before + 1) : sb.length());                        } else {                            setSelection(start + count - before);                        }                    }                }                addTextChangedListener(watcher);            }        }        @Override        public void afterTextChanged(Editable s) {        }    };    public String getTextWithoutSpace(){        return super.getText().toString().replace(" ","");    }}



这是效果
这里写图片描述


看了该效果,如果现在让你再写个电话号码输入框,应该会写了吧?

/** * Created by hzlinxuanxuan on 2015/12/22. */public class PhoneInputEditText extends EditText implements ITextWithoutSpaceInter{    public PhoneInputEditText(Context context, AttributeSet attrs) {        super(context, attrs);        addTextChangedListener(watcher);        setFilters(new InputFilter[]{new InputFilter.LengthFilter(13)});    }    public PhoneInputEditText(Context context) {        this(context, null);    }    public PhoneInputEditText(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        addTextChangedListener(watcher);        setFilters(new InputFilter[]{new InputFilter.LengthFilter(13)});    }    private TextWatcher watcher = new TextWatcher() {        @Override        public void beforeTextChanged(CharSequence s, int start, int count, int after) {        }        @Override        public void onTextChanged(CharSequence s, int start, int before, int count) {            if (s == null) {                return;            }            //判断是否是在中间输入,需要重新计算            boolean isMiddle = (start + count) < (s.length());            //在末尾输入时,是否需要加入空格            boolean isNeedSpace = false;            if (!isMiddle && isNeedSpace(s.length())) {                isNeedSpace = true;            }            if (isMiddle || isNeedSpace) {                String newStr = s.toString();                newStr = newStr.replace(" ", "");                StringBuilder sb = new StringBuilder();                for (int i = 0; i < newStr.length(); ) {                    if (i == 0) {                        sb.append(newStr.length() > 3 ? newStr.substring(0, 3) : newStr);                        i += 3;                        continue;                    } else if (i > 0) {                        sb.append(" ");                        if (i + 4 <= newStr.length()) {                            sb.append(newStr.substring(i, i + 4));                        } else {                            sb.append(newStr.substring(i, newStr.length()));                        }                        i += 4;                    }                }                removeTextChangedListener(watcher);                setText(sb);                //如果是在末尾的话,或者加入的字符个数大于零的话(输入或者粘贴)                if (!isMiddle || count > 1) {                    setSelection(sb.length());                } else if (isMiddle) {                    //如果是删除                    if (count == 0) {                        //如果删除时,光标停留在空格的前面,光标则要往前移一位                        if (isNeedSpace(start - before + 1)) {                            setSelection((start - before) > 0 ? start - before : 0);                        } else {                            setSelection((start - before + 1) > sb.length() ? sb.length() : (start - before + 1));                        }                    }                    //如果是增加                    else {                        if (isNeedSpace(start - before + count)) {                            setSelection((start + count - before + 1) < sb.length() ? (start + count - before + 1) : sb.length());                        } else {                            setSelection(start + count - before);                        }                    }                }                addTextChangedListener(watcher);            }        }        @Override        public void afterTextChanged(Editable s) {        }    };    @Override    public String getTextWithoutSpace() {        return super.getText().toString().replace(" ", "");    }    private boolean isNeedSpace(int length) {        if (length < 4) {            return false;        } else if (length == 4) {            return true;        } else return (length + 1) % 5 == 0;    }}



这是效果
这里写图片描述

现在新需求来了,又来了一个身份证号码输入框,格式是:330304 1995 0406 1456

如果按照上面的方法再写个控件的话是可以解决问题,但是比较麻烦,所以将上述三种的输入框合并成一种,然后自定义属性,可以在xml中指定是哪一种类型,或者使用代码指定。直接上代码:

/** * Created by hzlinxuanxuan on 2015/12/22. * 该控件是支持输入时自带控件的,目前支持xml属性指定,也支持代码指定该输入卡所属的类型 * 现在支持:电话、卡号、身份证号,或者无类型(正常输入) */public class ContentWithSpaceEditText extends EditText{    private int contentType;    public static final int TYPE_PHONE = 0;    public static final int TYPE_CARD = 1;    public static final int TYPE_IDCARD = 2;    public ContentWithSpaceEditText(Context context) {        this(context, null);    }    public ContentWithSpaceEditText(Context context, AttributeSet attrs) {        super(context, attrs);        parseAttributeSet(context, attrs);    }    public ContentWithSpaceEditText(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        parseAttributeSet(context, attrs);    }    private void parseAttributeSet(Context context, AttributeSet attrs) {        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ContentWithSpaceEditText, 0, 0);        contentType = ta.getInt(R.styleable.ContentWithSpaceEditText_epaysdk_type, 0);        ta.recycle();        initType();        setSingleLine();        addTextChangedListener(watcher);    }    private void initType(){        if (contentType == TYPE_PHONE) {            setInputType(InputType.TYPE_CLASS_NUMBER);            setFilters(new InputFilter[]{new InputFilter.LengthFilter(13)});        } else if (contentType == TYPE_CARD) {            setInputType(InputType.TYPE_CLASS_NUMBER);            setFilters(new InputFilter[]{new InputFilter.LengthFilter(31)});        } else if (contentType == TYPE_IDCARD) {            setFilters(new InputFilter[]{new InputFilter.LengthFilter(21)});        }    }    public void setContentType(int contentType) {        this.contentType = contentType;        initType();    }    private TextWatcher watcher = new TextWatcher() {        @Override        public void beforeTextChanged(CharSequence s, int start, int count, int after) {        }        @Override        public void onTextChanged(CharSequence s, int start, int before, int count) {            if (s == null) {                return;            }            //判断是否是在中间输入,需要重新计算            boolean isMiddle = (start + count) < (s.length());            //在末尾输入时,是否需要加入空格            boolean isNeedSpace = false;            if (!isMiddle && isSpace(s.length())) {                isNeedSpace = true;            }            if (isMiddle || isNeedSpace) {                String newStr = s.toString();                newStr = newStr.replace(" ", "");                StringBuilder sb = new StringBuilder();                int spaceCount = 0;                for (int i = 0; i < newStr.length(); i++) {                    sb.append(newStr.substring(i, i+1));                    //如果当前输入的字符下一位为空格(i+1+1+spaceCount),因为i是从0开始计算的,所以一开始的时候需要先加1                    if(isSpace(i + 2 + spaceCount)){                        sb.append(" ");                        spaceCount += 1;                    }                }                removeTextChangedListener(watcher);                setText(sb);                //如果是在末尾的话,或者加入的字符个数大于零的话(输入或者粘贴)                if (!isMiddle || count > 1) {                    setSelection(sb.length());                } else if (isMiddle) {                    //如果是删除                    if (count == 0) {                        //如果删除时,光标停留在空格的前面,光标则要往前移一位                        if (isSpace(start - before + 1)) {                            setSelection((start - before) > 0 ? start - before : 0);                        } else {                            setSelection((start - before + 1) > sb.length() ? sb.length() : (start - before + 1));                        }                    }                    //如果是增加                    else {                        if (isSpace(start - before + count)) {                            setSelection((start + count - before + 1) < sb.length() ? (start + count - before + 1) : sb.length());                        } else {                            setSelection(start + count - before);                        }                    }                }                addTextChangedListener(watcher);            }        }        @Override        public void afterTextChanged(Editable s) {        }    };    public String getTextWithoutSpace() {        return super.getText().toString().replace(" ", "");    }    public boolean checkTextRight(){        String text = getTextWithoutSpace();        //这里做个简单的内容判断        if (contentType == TYPE_PHONE) {            if (TextUtils.isEmpty(text)) {                ToastUtil.show(getContext(), "手机号不能为空,请输入正确的手机号");            } else if (text.length() < 11) {                ToastUtil.show(getContext(), "手机号不足11位,请输入正确的手机号");            } else {                return true;            }        } else if (contentType == TYPE_CARD) {            if (TextUtils.isEmpty(text)) {                ToastUtil.show(getContext(), "银行卡号不能为空,请输入正确的银行卡号");            } else if (text.length() < 14) {                ToastUtil.show(getContext(), "银行卡号位数不正确,请输入正确的银行卡号");            } else {                return true;            }        } else if (contentType == TYPE_IDCARD) {            if (TextUtils.isEmpty(text)) {                ToastUtil.show(getContext(), "身份证号不能为空,请输入正确的身份证号");            } else if (text.length() < 18) {                ToastUtil.show(getContext(), "身份证号不正确,请输入正确的身份证号");            } else {                return true;            }        }        return false;    }    private boolean isSpace(int length) {        if (contentType == TYPE_PHONE) {            return isSpacePhone(length);        } else if (contentType == TYPE_CARD) {            return isSpaceCard(length);        } else if (contentType == TYPE_IDCARD) {            return isSpaceIDCard(length);        }        return false;    }    private boolean isSpacePhone(int length) {        if (length < 4) {            return false;        } else if (length == 4) {            return true;        } else return (length + 1) % 5 == 0;    }    private boolean isSpaceCard(int length) {        return length % 5 == 0;    }    private boolean isSpaceIDCard(int length) {        if (length <= 6) {            return false;        } else if (length == 7) {            return true;        } else return (length - 2) % 5 == 0;    }}



style.xml

    <declare-styleable name="ContentWithSpaceEditText">        <attr name="epaysdk_type" format="enum">            <enum name="phone" value="0" />            <enum name="card" value="1" />            <enum name="IDCard" value="2" />        </attr>    </declare-styleable>


代码不详述,比较下上述的代码就懂了。

具体的用法:

<com.think.example.ContentWithSpaceEditText            android:layout_width="fill_parent"            android:layout_height="wrap_content"            android:hint="请输入银行卡号"            android:textSize="14dp"            app:epaysdk_type="card"/>


QA测了几天,发现在身份证情况下,输入一会儿会导致输入法的跳转。bug重现步骤:

1.在身份证情况下,因为允许输入数字和字母,所以未对InputType进行设置。

2.身份证的格式为****** **** **** ****,如果我先使用数字键盘输入,输入123456,此时再输入的时候(此时输入框会自动加一个空格),输入法会自动跳转到英文字母键盘,这会让用户很奇怪,为什么输入法突然跳转了。事实上在每次重排输入框内容的时候(例如添加空格,或者中间插入),就会出现该问题。

后来定位到setText该方法会导致重新唤起输入法,具体解决方法请看另外一篇文章。Android中EditText.setText(String)方法导致输入法跳转

阅读全文
0 0
原创粉丝点击