从LengthFilter理解InputFilter的filter参数含义

2,956 阅读3分钟

背景

需要实现EditText限制字数需求,直接在xml中设置android:maxLength属性就能解决;但是产品要求输入超出最大数的时候要Toast提示用户,并且还要阻止用户继续输入。**

初始方案是通过实现TextWatcher来处理,可以实现主要的需求逻辑,还是遇到了问题

  1. 监听和重新设置处理之后的文本时,会再次触发TextWatcher的回调
  2. 处理输入文本替换选中文本的逻辑比较复杂

问题1还比较好处理,在onTextChanged方法中处理文本,处理之前先移除自身的TextWatcher对象,在处理完文本并重新设置到EditText上之后再重新添加;对问题2直接选择放弃。

分析

既然有maxLength的属性,那就看看源码是怎么实现的。

跟踪一下获取maxLength的属性的代码会发现,是通过设置InputFilter对象来实现的,具体实现就是InputFilter.LengthFilter,源码如下:

    public static class LengthFilter implements InputFilter {
        @UnsupportedAppUsage
        private final int mMax;

        public LengthFilter(int max) {
            mMax = max;
        }

        /**
         * @param source    输入的内容
         * @param start     输入的内容的起始位置,即0
         * @param end       输入的内容的结束位置,也就是source的长度
         * @param dest      已存在的内容
         * @param dstart    光标在已存在的内容的起始位置
         * @param dend      光标在已存在的内容的结束位置
         *
         * @return 本次输入的内容
         */
        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);
            }
        }

        /**
         * @return the maximum length enforced by this input filter
         */
        public int getMax() {
            return mMax;
        }
    }

看源码处理的逻辑思路,如下图;

LengthFilter流程图.jpg

int keep = mMax - (dest.length() - (dend - dstart));其中dest.length() - (dend - dstart)是计算当前文本中去掉将要被替换的字符后剩余的字符数;下图中,使用"第三个字"替换原始文本中的"之",这里限制了最大长度为5,keep最后的值就是5-(4-(2-1))=2

图1

最后将会从输入的"第三个字"四个字符中截取0到2的字符保留,对选中的"之"进行替换,结果如下:

图2

小结

public CharSequence filter(CharSequence source, int start, int end, Spanned dest,int dstart, int dend)

  • source:输入的内容
  • start:输入的内容的起始位置,即0
  • end:输入的内容的结束位置,也就是source的长度
  • dest:已存在的内容
  • dstart:光标在已存在的内容的起始位置
  • dend:光标在已存在的内容的结束位置

使用InputFilter可以对文本的输入做一些特殊的判断和过滤操作,但是并不能完全的控制文本的内容。

对项目的中解决方式也就很简单了,直接copy了LengthFilter的代码,在截取超出文本的地方做相应的逻辑处理。

写下来就是整理一点点只是做一下记录,不对的地方还请大佬指正