使用EditText的過程中,有時會遇到disable(禁止輸入)的需求,直接使用setEnabled(false)是無效的。網上搜尋發現可以通過設定InputFilter來實現
myEditText.setInputFilters(new InputFilter[]{ new InputFilter() { @Override filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { return dest.subSequence(dstart, dend); } }});
那麼到底是如何?disable的呢?先來看看InputFilter介面實作類別的介紹
InputFilter.AllCaps
--This filter will capitalize all the lower case letters that are added through edits
InputFilter.LengthFillter
--This filter will constrain edits not to make the length of the text greater than the specified length
NumberKeyListener
--For numeric text entry
DateKeyListener
--For entering dates in a text field
DateTimeKeyListener
--For entering dates and times in the same text field
DialerKeyListener
--For dialing-only text entry
DigitsKeyListener
--For digits-only text entry
TimeKeyListener
--For entering times in a text field
LoginFilter
--Abstract class for filtering login-related text (user names and passwords)
LoginFilter.PasswordFilterGMail
--This filter is compatible with GMail passwords which restricts characters to the Latin-1 (ISO8859-1) char set
LoginFilter.UsernameFilterGMail
--This filter rejects characters in the user name that are not compatible with GMail account creation
LoginFilter.UsernameFilterGeneric
--This filter rejects characters in the user name that are not compatible with Google login
可以看出,InputFilter主要是對使用者的(輸入)文本進行過濾的,這個介面只有一個filter方法
先看看InputFilter.AllCaps的實現原理
public static class AllCaps implements InputFilter { public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { for (int i = start; i < end; i++) { if (Character.isLowerCase(source.charAt(i))) { char[] v = new char[end - start]; TextUtils.getChars(source, start, end, v, 0); String s = new String(v).toUpperCase(); if (source instanceof Spanned) { SpannableString sp = new SpannableString(s); TextUtils.copySpansFrom((Spanned) source, start, end, null, sp, 0); return sp; } else { return s; } } } return null; // keep original } }
從代碼不難發現,當字串source中包含小寫字母時,將其轉換為大寫字母然後返回,從而實現AllCaps。
再來看看InputFilter.LengthFillter的實現原理
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { int keep = mMax - (dest.length() - (dend - dstart)); if (keep <= 0) { return ""; } else if (keep >= end - start) { return null; // keep original } else { keep += start; if (Character.isHighSurrogate(source.charAt(keep - 1))) { --keep; if (keep == start) { return ""; } } return source.subSequence(start, keep); } }
其中mMax是文本的長度上限,keep是mMax與當前text的長度的差值。
- 當keep小於0時,也就是文本已經超過長度上限時,返回""
- 當文本沒超過長度上限時,返回null,保持文本原樣不變(keep original)
- 最後一個else暫時可以不考慮
再來看看EditText是如何使用這個InputFilter的,其實InputFilter是在TextView中用到的,在TextView的setText()方法中
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; } }... sendOnTextChanged(text, 0, oldlen, textLength); onTextChanged(text, 0, oldlen, textLength);
遍曆所有的InputFilter,如果filter後的結果不是null,更新text變數。這意味著filter返回null就代表保持原文本不變(如上,在InputFilter.LengthFilter中看到的)。所有InputFilter後的結果就是最終顯示在TextView中的文本。
瞭解了InputFilter的原理,就知道如何對TextView/EditText的文本做“手腳”了。比如
- 不響應所有輸入的特殊字元(@#$%^&)
- 顯示最多隻能輸入100個大寫字母(使用InputFilter.LengthFilter和InputFilter.Allcaps的組合)
- 將輸入的某個字元自動替換為其他字元
回頭再看看disable EditText的那個InputFilter,對EditText來說,調用filter()的後三個參數始終是EMPTY_SPANNED, 0, 0,當執行filter時,就相當於執行EMPTY_SPANNED.subSequence(0, 0),永遠返回"",從而實現了disable。