1. 项目需求:

输入框限制输入内容为英文字符,数字,中文;长度限制最多6个字符

2. 需求拆分:

1> 字符类型限制
2> 字符长度限制

3. 方案实施:

1> 字符类型通过正则表达式进行过滤

//类型过滤器
        InputFilter typeFilter = new InputFilter() {
            @Override
            public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
                //1. 数字,英文字符,中文字符正则表达式
                Pattern p = Pattern.compile("[0-9a-zA-Z|\u4e00-\u9fa5]+");
                Matcher m = p.matcher(source.toString());
                //2. 未匹配到的字符,返回""进行过滤
                if (!m.matches()) return "";
                return null;
            }
        };
		//将过滤器添加至EditText中
        editText.setFilters(new InputFilter[]{typeFilter});

2> 字符长度直接通过TextWatcher回调中的afterTextChanged判断过滤

editText.addTextChangedListener(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) {
            }

            @Override
            public void afterTextChanged(Editable s) {
                Log.v(TAG, "afterTextChanged() called with: s = [" + s.toString() + "]");
                //1. 判断输入字符长度
                if(s.toString().length() > STR_LENGTH_LIMIT) {
                    //2. 若长度大于限制,则截取限制长度
                    editText.setText(s.toString().substring(0,STR_LENGTH_LIMIT));
                    //3. 将光标移至末尾位置
                    editText.setSelection(editText.getText().length());
                }
            }
        });

Android AppCompatEditText输入文本最大长度_ide

4. 方案问题

当输入中文时,由于输入框中会先键入拼音字母的选中值,此时也会触发afterTextChanged回调,导致拼音还未输入完成但已达到最大字符限制后,直接会被打断,eg:字符限制长度为6,当输入“我叫小明”,相应键入的拼音为“wojiaoxiaoming”,当输入到“wojiao”后,后面输入的内容就会打断拼音输入,直接键入所输入的字符;因为afterTextChanged中已经已经获取到了“wojiao”长度为6,后面再输入时导致字符直接被截断

Android AppCompatEditText输入文本最大长度_Text_02


拼音输入中文时,回调打印

2021-12-10 14:29:44.627 4497-4497/com.boom.inputmethodtest V/EditTextDemo: afterTextChanged() called with: s = []
2021-12-10 14:29:46.846 4497-4497/com.boom.inputmethodtest V/EditTextDemo: afterTextChanged() called with: s = [w]
2021-12-10 14:29:47.081 4497-4497/com.boom.inputmethodtest V/EditTextDemo: afterTextChanged() called with: s = [wo]
2021-12-10 14:29:47.348 4497-4497/com.boom.inputmethodtest V/EditTextDemo: afterTextChanged() called with: s = [woj]
2021-12-10 14:29:47.591 4497-4497/com.boom.inputmethodtest V/EditTextDemo: afterTextChanged() called with: s = [woji]
2021-12-10 14:29:47.733 4497-4497/com.boom.inputmethodtest V/EditTextDemo: afterTextChanged() called with: s = [wojia]
2021-12-10 14:29:47.968 4497-4497/com.boom.inputmethodtest V/EditTextDemo: afterTextChanged() called with: s = [wojiao]
//该次输入后字符便被截断
2021-12-10 14:29:49.511 4497-4497/com.boom.inputmethodtest V/EditTextDemo: afterTextChanged() called with: s = [wojiaox]
2021-12-10 14:29:49.530 4497-4497/com.boom.inputmethodtest V/EditTextDemo: afterTextChanged() called with: s = [wojiao]

5. 解决思路

1> 对于此问题,要解决的就是如何在通过拼音输入中文时,使拼音的待输入字符做到去除afterTextChanged的回调,通过3.1>中对非需求字符的过滤,得到启发,是否可以寻找一种特殊规则将拼音输入的字符进行过滤从而达到去除afterTextChanged回调的效果
2> 根据第一点的思路,先找出拼音输入时的特征,发现拼音输入时字符的体现形式预纯英文字符/数字有所区别,拼音键入时的字符会有下划线标识(不同输入法可能表现不一致),那就可以通过此标识来实现一个标识过滤器
3> 对于这种下划线效果,基本都是通过Spannable来进行实现的,因此我们可以通过判断字符的Span类型达到过滤效果

InputFilter spanFilter = new InputFilter() {
            @Override
            public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
                Log.d(TAG, "filter() called with: source = [" + source + " , " + source.getClass().getSimpleName() + "], start = [" + start + "], end = [" + end + "], dest = [" + dest + "], dstart = [" + dstart + "], dend = [" + dend + "]");
                SpannableString ss = new SpannableString(source);
                Object[] spanArray = ss.getSpans(0,ss.length() , Object.class);
                if(spanArray != null) {
                    for (int i = 0; i < spanArray.length; i++) {
                        if(spanArray[i] instanceof UnderlineSpan) {
                            return "";
                        }
                    }
                }
                return null;
            }
        };

如上我们通过判断字符Span类型,若查到某个字符以目标Span类型出现(此处为UnderlineSpan即下划线效果,其他场景需要根据各自输入法的效果进行适配,有的输入法为BackgroundColorSpan即背景色效果)则进行过滤;此时输入框内便不会显示待输入的字符串,也即不会有afterTextChanged回调。

当然这里也会引入一个体验性问题,用户无法看到他所键入的内容,只有输入法的联想词栏会有对应的内容显示,选中后便会键入输入框。

Android AppCompatEditText输入文本最大长度_java_03

ps:通过日志打印,也可以看到此处的CharSequence实例类为SpannableStringBuilder

2021-12-10 15:24:48.706 13209-13209/com.boom.inputmethodtest D/EditTextDemo: filter() called with: source = [w , SpannableStringBuilder], start = [0], end = [1], dest = [], dstart = [0], dend = [0]
2021-12-10 15:24:48.865 13209-13209/com.boom.inputmethodtest D/EditTextDemo: filter() called with: source = [wo , SpannableStringBuilder], start = [0], end = [2], dest = [], dstart = [0], dend = [0]
2021-12-10 15:24:49.617 13209-13209/com.boom.inputmethodtest D/EditTextDemo: filter() called with: source = [wos , SpannableStringBuilder], start = [0], end = [3], dest = [], dstart = [0], dend = [0]
2021-12-10 15:24:49.904 13209-13209/com.boom.inputmethodtest D/EditTextDemo: filter() called with: source = [wosh , SpannableStringBuilder], start = [0], end = [4], dest = [], dstart = [0], dend = [0]
2021-12-10 15:24:50.122 13209-13209/com.boom.inputmethodtest D/EditTextDemo: filter() called with: source = [woshi , SpannableStringBuilder], start = [0], end = [5], dest = [], dstart = [0], dend = [0]
2021-12-10 15:24:50.458 13209-13209/com.boom.inputmethodtest D/EditTextDemo: filter() called with: source = [woshix , SpannableStringBuilder], start = [0], end = [6], dest = [], dstart = [0], dend = [0]
2021-12-10 15:24:50.643 13209-13209/com.boom.inputmethodtest D/EditTextDemo: filter() called with: source = [woshixi , SpannableStringBuilder], start = [0], end = [7], dest = [], dstart = [0], dend = [0]
2021-12-10 15:24:50.794 13209-13209/com.boom.inputmethodtest D/EditTextDemo: filter() called with: source = [woshixia , SpannableStringBuilder], start = [0], end = [8], dest = [], dstart = [0], dend = [0]
2021-12-10 15:24:50.944 13209-13209/com.boom.inputmethodtest D/EditTextDemo: filter() called with: source = [woshixiao , SpannableStringBuilder], start = [0], end = [9], dest = [], dstart = [0], dend = [0]
2021-12-10 15:24:51.398 13209-13209/com.boom.inputmethodtest D/EditTextDemo: filter() called with: source = [woshixiaom , SpannableStringBuilder], start = [0], end = [10], dest = [], dstart = [0], dend = [0]
2021-12-10 15:24:52.774 13209-13209/com.boom.inputmethodtest D/EditTextDemo: filter() called with: source = [woshixiaomi , SpannableStringBuilder], start = [0], end = [11], dest = [], dstart = [0], dend = [0]
2021-12-10 15:24:53.071 13209-13209/com.boom.inputmethodtest D/EditTextDemo: filter() called with: source = [woshixiaomin , SpannableStringBuilder], start = [0], end = [12], dest = [], dstart = [0], dend = [0]
2021-12-10 15:24:53.324 13209-13209/com.boom.inputmethodtest D/EditTextDemo: filter() called with: source = [woshixiaoming , SpannableStringBuilder], start = [0], end = [13], dest = [], dstart = [0], dend = [0]

最终效果:

Android AppCompatEditText输入文本最大长度_android_04

以上便是该问题的整体解决思路,正如后面所讲的,此优化方式会出现拼音法输入中文时,用户键入的拼音不可见;如各位有更好的解决思路,欢迎沟通留言,THX!