输入手机号码自动填充空格 变成3-4-4格式

电话号码3-4-4格式实现方式

在最近的开发中遇到将电话号码展示成3-4-4格式的需求(如:132 2222 3333这样子),乍一看这个要求挺简单的,因为只需要在满足长度的字符串固定位置加上相应的空格即可。但实际上还是蛮复杂的,例如:case1、当删除时,假如说现在字符串为132 4,在删除这个4字符的时候需要将空格一起删除;也就是说删除过程中并不认为空格是占一个字符的,只需要点击一次删除就可以删除数字和其前面的空格;case2、光标位置的处理,这个case比较多,在下面会详细列举出来,

OK,下面就可以开始这个功能的设计了,首先肯定是在edittext中去做这些事情,对其addTextChangedListener,所有的操作都在下面这三个复写的方法中国实现,

editText.addTextChangedListener(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) {

            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

在做的时候我的实现策略是:

一、

由于光标比较麻烦,先实现添加空格和删除空格的功能,所以这时候都默认光标位于字符串的尾部。具体的做法是,在edittext每次发生变化后去拿到其中的字符串,然后将其中的空格(不管有没有空格)全部给去掉,最后再遍历这个字符串,根据字符串的长度去在规定的位置添加空格,如果字符串长度小于或则等于时的话,就不符合添加条件。

这时候我们需要考虑上述的case1,如果现在edittext中是“132 4567 8”这样的字符串,这时候需要去删除8,同时要求把它前面的空格也删除掉。

那么我们现在来模拟一下上述策略下删除8时字符串的变化情况,首先变为“132 4567 ”,这时候去掉所有的空格变为“1324567”,这时候遍历字符串在对应的位置上添加空格后变为“132 4567 ”,这样无法把8和它前面的空格一起删掉,问题就出在这时候字符串的最后一位是空格,OK,我们可以在每次遍历加空格后,如果发现字符串最后一位是空格时,就去除空格。那样就可以完美解决case1的问题。具体实现是在afterTextChanged中实现的,代码如下:

@Override
            public void afterTextChanged(Editable s) {
                    String content = s.toString().replaceAll(" ", "");  //除去所有的空格

                    StringBuffer sb = new StringBuffer(content);
                    //遍历加空格
                    int index = 1;
                    emptyNumA = 0;
                    for (int i = 0; i < content.length(); i++) {
                        if (i == 2) {

                            sb.insert(i + index, " ");
                            index++;
                        } else if (i == 6) {
                            sb.insert(i + index, " ");
                            index++;
                        }
                    }
                    Log.i(TAG, "result content:" + sb.toString());
                    String result = sb.toString();
                    //假如说字符串后面有空格,除去空格
                    if (result.endsWith(" ")) {
                        result = result.substring(0, result.length() - 1);
                    }

二、

然后来考虑光标问题,首先在afterTextChanged中可以通过editText.getSelectionEnd()来获得当前光标selectIndex的位置。

1、填写电话号码添加空格时,这时候需要考虑将光标向后移动一位,在输入后面内容时才不会出现错乱的现象。具体的做法是,在beforeTextChanged中去拿到字符串中的空格数量为emptyNumA,然后在afterTextChanged中统计出添加的空格数量emptyNumB,如果在输入过程中添加了空格,就会出现emptyNumA < emptyNumB的情况,这时候需要将光标的位置增加(emptyNumA - emptyNumB)即可。2、例如”123 45”且光标在4后面 这时需要删除4 光标的处理,这时也可以去模拟变化,根据上面的策略变化后得到的字符串是“123 5”,而且光标位置在5前面且空格后面,但是显然不对呀,光标正确的位置应该是在3后面,OK。我们仔细观察可以发现,这种case只发生在删除后光标前一个位置是空格,且后面位置有数据,我们可以用一下的逻辑来控制:

if (selectIndex > 1 && s.charAt(selectIndex - 1) == ' ') {
                        selectIndex--;
                    }

然后又经过我自己的一些测试,发现另外一个问题,例如“123 4”,光标在3后面且在空格前面,这时候去输入数字5时,模拟出来的结果是“123 54”且光标在5前面空格后面,显然又不对了,光标应该在的位置是5后面4前面,同样问题还是出现在,结果字符串的光标前一个位置是空格,这时候需要做的是将光标向后移动一个位置(selectIndex++)。这和上面的区别是,上面问题是删除字符串导致的,这个是添加字符导致的。OK,这个问题我们可以通过判断editext中的字符串是在增加还是在减少来判断是selectIndex++还是selectIndex—。

OK,解决了这些小问题,可以贴出完整的代码了。

edittext.addTextChangedListener(new TextWatcher() {

            private int oldLength = 0;
            private boolean isChange = true;
            private int curLength = 0;
            private int emptyNumB = 0;  //初始空格数
            private int emptyNumA = 0;  //遍历添加空格后的空格数

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                oldLength = s.length();
                Log.i(TAG, "未改变长度: " + oldLength);
                emptyNumB = 0;
                for (int i = 0; i < s.toString().length(); i++) {
                    if (s.charAt(i) == ' ') emptyNumB++;
                }
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                curLength = s.length();
                Log.i(TAG, "当前长度: " + curLength);
                //优化处理,如果长度未改变或则改变后长度小于3就不需要添加空格
                if (curLength == oldLength || curLength <= 3) {
                    isChange = false;
                } else {
                    isChange = true;
                }
            }

            @Override
            public void afterTextChanged(Editable s) {
                if (isChange) {
                    if (curLength - sLastLength < 0) {  //判断是editext中的字符串是在减少 还是在增加
                        delete = true;
                    } else {
                        delete = false;
                    }
                    sLastLength = curLength;
                    int selectIndex = edittext.getSelectionEnd();//获取光标位置
                    String content = s.toString().replaceAll(" ", "");
                    Log.i(TAG, "content:" + content);
                    StringBuffer sb = new StringBuffer(content);
                    //遍历加空格
                    int index = 1;
                    emptyNumA = 0;
                    for (int i = 0; i < content.length(); i++) {
                        if (i == 2) {

                            sb.insert(i + index, " ");
                            index++;
                            emptyNumA++;
                        } else if (i == 6) {
                            sb.insert(i + index, " ");
                            index++;
                            emptyNumA++;
                        }
                    }
                    Log.i(TAG, "result content:" + sb.toString());
                    String result = sb.toString();
                    //遍历加空格后 如果发现最后一位是空格 就把这个空格去掉
                    if (result.endsWith(" ")) {
                        result = result.substring(0, result.length() - 1);
                        emptyNumA--;
                    }

                  //为edittext设置处理后的字符串
                    edittext.setText(result);


                    //处理光标位置
                    if (emptyNumA > emptyNumB){
                        selectIndex = selectIndex + (emptyNumA - emptyNumB);
                    }
                    if (selectIndex > result.length()) {
                        selectIndex = result.length();
                    } else if (selectIndex < 0) {
                        selectIndex = 0;
                    }
                    // 例如"123 45"且光标在4后面 这时需要删除4 光标的处理
                    if (selectIndex > 1 && s.charAt(selectIndex - 1) == ' ') {
                        if (delete) {
                            selectIndex--;
                        } else {
                            selectIndex++;
                        }
                    }

                    edittext.setSelection(selectIndex);
                    isChange = false;
                }
            }
        });

讲道理,这时候已经OK了,可以完美运行,但是我自己在测的时候又发现了一个问题:在以小米为首的一些手机上,长按软键盘上的删除键时,我希望的结果是可以一直删除edittext上的字符串,但现实是,只能删除一次,然后软键盘上的删除按钮的焦点就没了。

经过一顿查找,终于找到问题所在,问题出在这句

 //为edittext设置处理后的字符串
    edittext.setText(result);

需要将这句改成以下这句即可

/**
                     * 用遍历添加空格后的字符串 来替换editext变化后的字符串
                     */
                    s.replace(0, s.length(), result);

ok,这样这个电话号码3-4-4自动添加空格的功能就完成了。

ps:我们还可以把它改成添加银行卡自动填写空格的demo,区别是需要改掉遍历加空格for循环的那部分代码,改为如下所示:

for (int i = 0; i < content.length(); i++) {
                        if ((i + 1) % 4 == 0) {
                            sb.insert(i + index, " ");
                            index++;
                            emptyNumA++;
                        }
                    }

这样子的话就和支付宝中添加银行卡的功能和行为相同了。
源码地址:
https://github.com/jpingTang/autoAddSpacingForPhoneNumber3-4-4

最后感谢这位博主在最开始提供的思路:
http://blog.csdn.net/yyeeqe_sy/article/details/50697922

你可能感兴趣的:(android)