这篇文章的起因请先看我的另外一篇文章(有点长,都是代码惹的祸)
http://blog.csdn.net/lintcgirl/article/details/50358421
简要说明就是为EditText设置了一个TextWatcher,在onTextChanged方法中监听新的text,然后根据我想要的效果计算出一个Stringbuilder,然后调用setText方法。
如果我使用数字键盘输入,当EditText调用了setTex方法的话,会导致输入法的跳转(从数字键盘跳到了字母键盘)。
于是我参照了另外一篇博客http://blog.csdn.net/junjun071308/article/details/47305435
他的做法就是在afterTextChanged方法中不去调用setText方法,直接修改该方法中的形参,因为对该形参的修改会直接影响到EditText中的内容,所以其实并不用调用setText。
于是我尝试用这种方法去解决。将onTextChanged方法中的内容移到了afterTextChanged方法中,代码不变,只是在onTextChanged方法中记录了start、count、before参数,用在afterTextChanged方法中。
看代码:
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) {
ContentWithSpaceEditText.this.start = start;
ContentWithSpaceEditText.this.before = before;
ContentWithSpaceEditText.this.count = count;
}
@Override
public void afterTextChanged(Editable s) {
if (s == null) {
return;
}
//判断是否是在中间输入,需要重新计算
boolean isMiddle = (start + count) < (s.length());
//在末尾输入时,是否需要加入空格
boolean isNeedSpace = false;
if (!isMiddle && isSpace(s.length())) {
isNeedSpace = true;
}
if (isMiddle || isNeedSpace || count > 1) {
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)。
* 但是如果调用该语句的话,在身份证输入框的情况下(允许字母和数字),当调用setText时,会导致输入法的跳转
* 参照网上解决方法,将该句话替换成s.replace(...)
* 该种方法不会导致输入法的跳转。
* 造成输入法跳转的原因可能是setText会重新唤起输入法控件*/
s.replace(0, s.length(),sb);
//如果是在末尾的话,或者加入的字符个数大于零的话(输入或者粘贴)
if (!isMiddle || count > 1) {
setSelection(s.length() <= maxLength ? s.length() : maxLength);
} else if (isMiddle) {
//如果是删除
if (count == 0) {
//如果删除时,光标停留在空格的前面,光标则要往前移一位
if (isSpace(start - before + 1)) {
setSelection((start - before) > 0 ? start - before : 0);
} else {
setSelection((start - before + 1) > s.length() ? s.length() : (start - before + 1));
}
}
//如果是增加
else {
if (isSpace(start - before + count)) {
setSelection((start + count - before + 1) < s.length() ? (start + count - before + 1) : s.length());
} else {
setSelection(start + count - before);
}
}
}
addTextChangedListener(watcher);
}
}
};
没多大变,就是把setText(sb)改成了s.replace(0, s.length(),sb);
本以为万事大吉,但是发现这样一改,身份证输入控件是可以了,但是对于电话号码和卡号就不行了,原因是在执行s.replace(0, s.length(),sb)时,因为在电话号码和卡号中设置了InputType类型为Number,在replace的时候,会自动帮你把空格给删去。举个例子:
在输入电话号码时,格式为### #### ####,在输入123时,并不会触发replace函数。如果此时再输入4,s变量的值传进来为”1234”,计算出的sb为”123 4”,然后去执行replace函数,因为设置InputType,在replace的时候,将空格删去了,所以s还是等于”1234”,虽然这句执行不会出错,但是在下面setSelection的时候,会抛出IndexOutOfRangeException,因为setSelection函数的参数是sb.length(),s的长度为4,sb的长度为5,当然会出错了。。。。
于是我为卡号和电话号码添加了输入字符的限制,可输入的字符包括”0123456789 “,重点是有空格哦,所以就可以了。
设置输入字符的代码:
setKeyListener(DigitsKeyListener.getInstance(“0123456789 “));
这里需要注意的是,setKeyListener需要在setInputType方法之后调用,否则无效。。。
这里读者会疑问,为什么原来电话和卡号输入的时候,setText(带有空格的字符串)就可以生效,为什么replace的时候就无效。首先这是一个好问题,带读者做个例子就知道了。
首先写一个界面,上面有一个输入框,在布局文件中设置inputType为number,然后我再MainActivity中为该输入框setText(“hello world”),运行该应用,结果是该输入框显示hello world。这是什么鬼,不是已经设置了InputType么。
带你看下setText源码
该代码是private void setText(CharSequence text, BufferType type,boolean notifyBefore, int oldlen);方法中的片段。在EditText下,第二个参数type的值为BufferType.EDITABLE。
看代码,如果type为BufferType.EDITABLE的话,会根据传进来的text重新生成一个Editable,生成后才为他设置Filter,但是Filter只有在改变的时候,才会调用Filter的方法,所以Filter对初始的值也没有什么办法,所以setText的时候会成功。
接下来给大家看看整理后的源码:
/**
* Created by hzlinxuanxuan on 2015/12/22.
* 该控件是支持输入时自带控件的,目前支持xml属性指定,也支持代码指定该输入卡所属的类型
* 现在支持:电话、卡号、身份证号,或者无类型(正常输入)
*/
public class ContentWithSpaceEditText extends CleanUpEditText{
private int contentType;
public static final int TYPE_PHONE = 0;
public static final int TYPE_CARD = 1;
public static final int TYPE_IDCARD = 2;
private int maxLength = 100;
private int start, count,before;
private String digits;
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) {
maxLength = 13;
digits = "0123456789 ";
setInputType(InputType.TYPE_CLASS_NUMBER);
} else if (contentType == TYPE_CARD) {
maxLength = 31;
digits = "0123456789 ";
setInputType(InputType.TYPE_CLASS_NUMBER);
} else if (contentType == TYPE_IDCARD) {
maxLength = 21;
digits = null;
setInputType(InputType.TYPE_CLASS_TEXT);
}
setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
}
@Override
public void setInputType(int type) {
if (contentType == TYPE_PHONE || contentType == TYPE_CARD) {
type = InputType.TYPE_CLASS_NUMBER;
}else if(contentType == TYPE_IDCARD){
type = InputType.TYPE_CLASS_TEXT;
}
super.setInputType(type);
/* 非常重要:setKeyListener要在setInputType后面调用,否则无效。*/
if(!TextUtils.isEmpty(digits)) {
setKeyListener(DigitsKeyListener.getInstance(digits));
}
}
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) {
ContentWithSpaceEditText.this.start = start;
ContentWithSpaceEditText.this.before = before;
ContentWithSpaceEditText.this.count = count;
}
@Override
public void afterTextChanged(Editable s) {
if (s == null) {
return;
}
//判断是否是在中间输入,需要重新计算
boolean isMiddle = (start + count) < (s.length());
//在末尾输入时,是否需要加入空格
boolean isNeedSpace = false;
if (!isMiddle && isSpace(s.length())) {
isNeedSpace = true;
}
if (isMiddle || isNeedSpace || count > 1) {
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)。
* 但是如果调用该语句的话,在身份证输入框的情况下(允许字母和数字),当调用setText时,会导致输入法的跳转
* 参照网上解决方法,将该句话替换成s.replace(...)
* 该种方法不会导致输入法的跳转。
* 造成输入法跳转的原因可能是setText会重新唤起输入法控件*/
s.replace(0, s.length(),sb);
//如果是在末尾的话,或者加入的字符个数大于零的话(输入或者粘贴)
if (!isMiddle || count > 1) {
setSelection(s.length() <= maxLength ? s.length() : maxLength);
} else if (isMiddle) {
//如果是删除
if (count == 0) {
//如果删除时,光标停留在空格的前面,光标则要往前移一位
if (isSpace(start - before + 1)) {
setSelection((start - before) > 0 ? start - before : 0);
} else {
setSelection((start - before + 1) > s.length() ? s.length() : (start - before + 1));
}
}
//如果是增加
else {
if (isSpace(start - before + count)) {
setSelection((start + count - before + 1) < s.length() ? (start + count - before + 1) : s.length());
} else {
setSelection(start + count - before);
}
}
}
addTextChangedListener(watcher);
}
}
};
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) {
return length >= 4 && (length == 4 || (length + 1) % 5 == 0);
}
private boolean isSpaceCard(int length) {
return length % 5 == 0;
}
private boolean isSpaceIDCard(int length) {
return length > 6 && (length == 7 || (length - 2) % 5 == 0);
}
}