EditText限制輸入的幾種方式及只顯示中文漢字的做法
前段時間項目中有個需求是要求只能輸入漢字,並且不能輸入偏旁部首,於是總結了下EditText限制輸入的幾種方式,但是對於語音輸入的還沒找到好的解決方案:
- 通過設置EditText的inputType來限制,可以在xml或者java代碼中設置:
在xml中設置:android:inputType="textPassword"
在java代碼中設置: mEditText.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
可以通過設置不同屬性來限制輸入內容。
-
通過設置EditText的
android:digits
屬性來限制可輸入的內容,但需要把允許輸入的內容全都羅列出來,只適合允許輸入少數限制的情況,如只允許輸入數字,像這種只能輸入漢字的情況明顯不適合,總不能把幾千個漢字全都羅列出來吧。 -
通過InputFilter來限制。
/**
* EditText限制只能輸入漢字
*/
public InputFilter getInputFilter() {
InputFilter filter = new InputFilter() {
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
if (TextUtils.isEmpty(source)){
return "";
}
for (int i = start; i < end; i++) {
if (stringFilterChinese(source) && !source.toString().contains("。") && !source.toString ().contains(",")) {
return "";
} else if (CHINESE_RADICAL_DIGISTS.contains(source)) {
return "";
}
}
return null;
}
};
return filter;
}
/**
* 限制只能輸入漢字
*
* @param str 輸入值
*/
public boolean stringFilterChinese(CharSequence str) {
//只允許漢字
String regEx = "[^\u4E00-\u9FA5]";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(str);
if (m.find()) {
return true;
} else {
return false;
}
}
mEdtAddDictation.setFilters(new InputFilter[]{getInputFilter()});
查看TextView的源碼,在setText中通過調用filter()過濾了相關內容:
private void setText(CharSequence text, BufferType type,boolean notifyBefore, int oldlen) {
...
int n = mFilters.length;
for (int i = 0; i < n; i++) {
CharSequence out = mFilters[i].filter(text, 0, text.length(), EMPTY_SPANNED, 0, 0);
if (out != null) {
text = out;
}
}
...
}
- 通過TextWatch來限制輸入。
mEdtAddDictation.addTextChangedListener(mTextWatcher);
private TextWatcher mTextWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
this.temp = s;
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable editable) {
String words = editable.toString().trim();
if (TextUtils.isEmpty(words)) {
mBtnAddSure.setEnabled(false);
} else {
mBtnAddSure.setEnabled(true);
}
if (TextUtils.isEmpty(words)) {
return;
}
String newWords = words;
newWords = StringUtils.clearLimitStr(StringUtils.DEFAULT_REGEX_LIMIT_CHINESE,newWords);
newWords = StringUtils.clearLimitStr(StringUtils.CHINESE_RADICAL_DIGISTS,newWords);
boolean isChange = false;
if (!TextUtils.equals(words,newWords)){
isChange = true;
words = newWords;
}
if (words.length() > MAX_INPUT_LIMIT) {
ToastUtils.getInstance(AddDictationWordsActivity.this).s(R.string.dictation_add_word_limit);
words = words.substring(0, MAX_INPUT_LIMIT);
isChange = true;
}
if (isChange) {
mEdtAddDictation.removeTextChangedListener(this);
// et.setText方法可能會引起鍵盤變化,所以用editable.replace來顯示內容
editable.replace(0, editable.length(), words.trim());
mEdtAddDictation.addTextChangedListener(this);
}
}
};
/**
* 清除不符合條件的內容
*
* @param regex
* @return
*/
public static String clearLimitStr(String regex, String str) {
return str.replaceAll(regex, "");
}
/**
* 默認的篩選條件(正則:只能輸入中文)
*/
public static String DEFAULT_REGEX_LIMIT_CHINESE = "[^\u4E00-\u9FA5]";
/**
* 偏旁部首
*/
public static final String CHINESE_RADICAL_DIGISTS = "[犭凵巛冖氵廴糹訁礻亻釒宀亠忄辶弋飠刂阝冫卩疒艹疋豸冂匸扌丬屮衤勹彳彡]";
- 通過自定義InputConnectionWrapper來限制輸入。
步驟:
(1)自定義EditText,重載onCreateInputConnection方法,它需要返回一個InputConnection對象;
(2)繼承於InputConnectionWrapper, 實現自己的InputConnection 並且在onCreateInputConnection中返回。
(3)在自定義的InputConnectionWrapper類中,實現輸入法輸入和按鍵事件的攔截。
由於InputConnection是在文本顯示之前進行調用,因此可以通過重寫其中的方法修改要顯示的內容。
/**
* @author zhangshao
* @desc 只能輸入漢字的輸入框
* @time 2018/11/8 18:09
*/
@SuppressLint("AppCompatCustomView")
public class ChineseLimitEditText extends EditText {
public ChineseLimitEditText(Context context) {
super(context);
}
public ChineseLimitEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ChineseLimitEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 輸入法
*
* @param outAttrs
* @return
*/
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return new InnerInputConnecttion(super.onCreateInputConnection(outAttrs),
false);
}
class InnerInputConnecttion extends InputConnectionWrapper implements InputConnection {
public InnerInputConnecttion(InputConnection target, boolean mutable) {
super(target, mutable);
}
/**
* 對輸入的內容進行攔截
*
* @param text
* @param newCursorPosition
* @return
*/
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
// 只能輸入漢字
if (!TextUtils.isEmpty(text) && (!StringUtils.isContainChinese(text.toString()) ||
StringUtils.isContainRadical(text.toString()))) {
return false;
}
return super.commitText(text, newCursorPosition);
}
@Override
public boolean sendKeyEvent(KeyEvent event) {
// 攔截換行鍵
return event.getKeyCode() != KeyEvent.KEYCODE_ENTER && super.sendKeyEvent(event);
}
@Override
public boolean setSelection(int start, int end) {
return super.setSelection(start, end);
}
}
}
/**
* 字符串是否包含中文
* */
public static boolean isContainChinese(String str) {
Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
Matcher m = p.matcher(str);
if (m.find()) {
return true;
}
return false;
}
/**
* 字符串是否包含偏旁部首
* */
public static boolean isContainRadical(String str) {
Pattern p = Pattern.compile(CHINESE_RADICAL_DIGISTS);
Matcher m = p.matcher(str);
if (m.find()) {
return true;
}
return false;
}
攔截條件:在commitText方法中,如果執行父類的 commitText(即super.commitText(text, newCursorPosition))那麼表示不攔截,如果返回false則表示攔截,
輸入法的字符串則無法傳送到EditText。在sendKeyEvent中,如果執行父類的sendKeyEvent(即super.sendKeyEvent(event))那麼表示不攔截,如果返回false表示攔截。
不同的需求可以通過不同的限制方法組合使用,不用侷限於一種。
以上幾種方法都可以解決軟鍵盤輸入時只顯示中文的問題,但是搜狗輸入法的語音輸入無法過濾,一旦在InputFilter或者TextWatch中屏蔽,那麼語音輸入內容會重複,目前分析的原因是:語音輸入是持續輸入,如果去掉相應的標點,那麼輸入法會檢測到輸入內容與緩存的不對應,會把之前的文本拿出來重新拼接在一起返回。如果有朋友有好的解決方案,還望不吝賜教!