Android限制输入框10个汉字或20个字母完美实现

在android 开发中,经常会遇到要求输入框内输入的文本内容限制10个汉字或20个字母的要求。之前也在网上看了很多解决方案,效果都不是很好,大多数都是直接取字符串的length()作为限定的判断依据,这个思路是非常不对的,单纯的用String.length(),去判断字符串的长度,字母和汉字的长度一样,因此我们要换一个思路去想。

根据“10个汉字或20个字母的要求”,我们可以看出,这里的汉字和字母的长度关系比值是1:2的关系,因此我们要对字符串进行编码,这个编码就是要实现这个汉字和字母1:2的比值关系,这里我们用GB2312编码,不同的编码格式,比值关系不一样,这里大家要注意!!!

例如:

	String content = editText.getText().toString();
	byte[] bt = content.getBytes("gb2312");
	这里就是拿到输入框里数据,并将字符串编码后放入byte[] 数组中。

    接下来就是要判断长度,这里注意,用“gb2312”编码,按上述“10个汉字或20个字母”要求,最大长度就是20
	
	if(bt.length >20)
	{
		要处理的操作....
	}

	这里大家好要注意一个地方,就是一旦字符串长度超了之后的截取和转换,以及光标的位置问题,特别是后者 很多人都会遗忘。
首先是截取,将超过的字符串截取,当然,这里一定要先编码后在截取,而且是byte[]数组截取,这里我向大家提供一个方法
	private byte[] subBytes(byte[] src, int begin, int count) {
    		byte[] bs = new byte[count];
    		System.arraycopy(src, begin, bs, 0, count);
    		return bs;
}
使用的时候直接:
	String str = editable.toString();
	byte[] bt2 = str.getBytes("gb2312");
	byte[] bt3 = subBytes(bt2,0,20);
	截取好后,当然也要把byte数组里的内容转成字符串形式展现啊,这里我向大家提供一个方法
	private String gbToString(byte[] data) {
    		String str = null;
    	try {
        str = new String(data, "gb2312");
    		} catch (UnsupportedEncodingException e) {
    	}
    	return str;
}
使用时直接
	String newStrs = gbToString(bt3);
	editText.setText(newStrs);
最后就是光标的问题,一定要放到最后面!!!
int selEndIndex = Selection.getSelectionEnd(editable);
//设置新光标所在的位置
Selection.setSelection(editable, selEndIndex);

当然,这个程序写到这,看似已经可以可以实现功能了,但若重新回顾一遍逻辑,还是能发现一个漏洞的。可能有人会不明白,我就直接分享一下BUG图吧:

为什么会出现这个BUG?其实关键就在字符串最后的截取上!汉字和字母的关系是1:2,如果用户输入9个汉字后,在输入一个字母,那么此时的长度19。这时候如果最后在输入一个汉字,那么长度就是21,截取时保留前20,导致最后一个汉字被切割一半,然后在转码成字符汉字时必然会乱码。怎样解决这个BUG?其实方法有很多,我也走过一些弯路,直到蹲坑无聊时突然想到一个很简单的处理方法。
1、既然是截取最后一个字符转换后出现的问题,那我们就判断转后的字符串最后一个字符是不是乱码的,如果是那就截掉,不是就保留。由于输入框只准保留汉字和英文字母,所以我们就直接判断最后一个字符是不是汉字或字母就好了。这里我提供两个判断方法,这两个方法我都放到StringUtils类中了

/**
 * 判断字符串是否包含中文
 *
 * @param str
 * @return
 */
public static boolean isContainChinese(String str) {
    Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
    Matcher m = p.matcher(str);
    return m.find();
}
/**
 * 判断字符串是否包含字母
 *
 * @param str
 * @return
 */
public static boolean isContainLetters(String str) {
    for (char i = 'A'; i <= 'Z'; i++) {
        if (str.contains(String.valueOf(i))) {
            return true;
        }
    }
    for (char i = 'a'; i <= 'z'; i++) {
        if (str.contains(String.valueOf(i))) {
            return true;
        }
    }
    return false;
}
判断方法有了,接下来就要把漏斗补上:

String temp = String.valueOf(newStrs.charAt(newStrs.length()-1));
boolean flag = StringUtil.isContainChinese(temp);
boolean flag2 = StringUtil.isContainLetters(temp);
if(!flag && !flag2 ){
    String newStrs2 = newStrs.substring(0,newStrs.length()-1);
    editText.setText(newStrs2);
}else {
    editText.setText(newStrs);
}
这里的二次截取的条件就是转换后的字符串最否一个字母即不是汉字也不是字母才会执行(如果各位还想包含其他字符,可以再加判断的)
现在再看看效果:

最后的乱码就被截取掉了。可以说很完美了。

还有一个知识点要提一下,关于文字的EditText文本监听,我个人认为用TextWatcher效果最好,因为它可以很方便自定义内容实现。
说了这么多,给大家奉献完整代码才是硬道理。
public class WatcherText implements TextWatcher {

    private int maxLen = 0;
    private EditText editText = null;

    public WatcherText(int maxLen, EditText editText) {
        this.maxLen = maxLen;
        this.editText = editText;
    }

    public void afterTextChanged(Editable arg0) {
        // TODO Auto-generated method stub

    }

    public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
                                  int arg3) {
        // TODO Auto-generated method stub

    }

    public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
        try {
            // TODO Auto-generated method stub
            Editable editable = editText.getText();
            String content = editText.getText().toString();

            byte[] bt = content.getBytes("gb2312");
            if(bt.length >maxLen)
            {
                ToastUtil.showErrorInfoTip("输入字符数超过限制");
                int selEndIndex = Selection.getSelectionEnd(editable);
                String str = editable.toString();
                byte[] bt2 = str.getBytes("gb2312");
                byte[] bt3 = subBytes(bt2,0,maxLen);
                String newStrs = gbToString(bt3);
                String temp = String.valueOf(newStrs.charAt(newStrs.length()-1));
                boolean flag = StringUtil.isContainChinese(temp);
                boolean flag2 = StringUtil.isContainLetters(temp);
                if(!flag && !flag2 ){
                    String newStrs2 = newStrs.substring(0,newStrs.length()-1);
                    editText.setText(newStrs2);
                }else {
                    editText.setText(newStrs);
                }
                editable = editText.getText();
                // 新字符串的长度
                int newLen = editable.length();
                // 旧光标位置超过字符串长度
                if(selEndIndex >= newLen)
                {
                    selEndIndex = editable.length();
                }
                //设置新光标所在的位置
                Selection.setSelection(editable, selEndIndex);

            }

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

    }

    private byte[] subBytes(byte[] src, int begin, int count) {
        byte[] bs = new byte[count];
        System.arraycopy(src, begin, bs, 0, count);
        return bs;
    }

    private String gbToString(byte[] data) {
        String str = null;
        try {
            str = new String(data, "gb2312");
        } catch (UnsupportedEncodingException e) {
        }
        return str;
    }

}


外面使用的时候直接
EditText editText = (EditText) findViewById(R.id.entry);
editText.addTextChangedListener(new WatcherText(MAX_NUM, editText));

如有不对的地方,欢迎各位大神指正,谢谢。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章