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));

如有不對的地方,歡迎各位大神指正,謝謝。

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