电话号码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