InputFilter源码的查看及使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/nongminkouhao/article/details/84101467

InputFilter源码

/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.text;

import android.annotation.NonNull;

import com.android.internal.util.Preconditions;

import java.util.Locale;

/**
 * InputFilters can be attached to {@link Editable}s to constrain the
 * changes that can be made to them.
 */
public interface InputFilter
{
    /**
     * This method is called when the buffer is going to replace the
     * range <code>dstart &hellip; dend</code> of <code>dest</code>
     * with the new text from the range <code>start &hellip; end</code>
     * of <code>source</code>.  Return the CharSequence that you would
     * like to have placed there instead, including an empty string
     * if appropriate, or <code>null</code> to accept the original
     * replacement.  Be careful to not to reject 0-length replacements,
     * as this is what happens when you delete text.  Also beware that
     * you should not attempt to make any changes to <code>dest</code>
     * from this method; you may only examine it for context.
     *
     * Note: If <var>source</var> is an instance of {@link Spanned} or
     * {@link Spannable}, the span objects in the <var>source</var> should be
     * copied into the filtered result (i.e. the non-null return value).
     * {@link TextUtils#copySpansFrom} can be used for convenience if the
     * span boundary indices would be remaining identical relative to the source.
     */
    public CharSequence filter(CharSequence source, int start, int end,Spanned dest, int dstart, int dend);

    /**
	 * 该过滤器会将输入的所有小写转大写(注意如果输入的没有小写就不转换,即使字符串的大写结果与字符串不同)
     * This filter will capitalize all the lowercase and titlecase letters that are added
     * through edits. (Note that if there are no lowercase or titlecase letters in the input, the
     * text would not be transformed, even if the result of capitalization of the string is
     * different from the string.)
     */
    public static class AllCaps implements InputFilter {
        private final Locale mLocale;

        public AllCaps() {
            mLocale = null;
        }

        /**
         * Constructs a locale-specific AllCaps filter, to make sure capitalization rules of that
         * locale are used for transforming the sequence.
         */
        public AllCaps(@NonNull Locale locale) {
            Preconditions.checkNotNull(locale);
            mLocale = locale;
        }

        public CharSequence filter(CharSequence source, int start, int end,
                                   Spanned dest, int dstart, int dend) {
            final CharSequence wrapper = new CharSequenceWrapper(source, start, end);

            boolean lowerOrTitleFound = false;
            final int length = end - start;
            for (int i = 0, cp; i < length; i += Character.charCount(cp)) {
                // We access 'wrapper' instead of 'source' to make sure no code unit beyond 'end' is
                // ever accessed.
                cp = Character.codePointAt(wrapper, i);
                if (Character.isLowerCase(cp) || Character.isTitleCase(cp)) {
                    lowerOrTitleFound = true;
                    break;
                }
            }
            if (!lowerOrTitleFound) {
                return null; // keep original
            }

            final boolean copySpans = source instanceof Spanned;
            final CharSequence upper = TextUtils.toUpperCase(mLocale, wrapper, copySpans);
            if (upper == wrapper) {
                // Nothing was changed in the uppercasing operation. This is weird, since
                // we had found at least one lowercase or titlecase character. But we can't
                // do anything better than keeping the original in this case.
                return null; // keep original
            }
            // Return a SpannableString or String for backward compatibility.
            return copySpans ? new SpannableString(upper) : upper.toString();
        }

        private static class CharSequenceWrapper implements CharSequence, Spanned {
            private final CharSequence mSource;
            private final int mStart, mEnd;
            private final int mLength;

            CharSequenceWrapper(CharSequence source, int start, int end) {
                mSource = source;
                mStart = start;
                mEnd = end;
                mLength = end - start;
            }

            public int length() {
                return mLength;
            }

            public char charAt(int index) {
                if (index < 0 || index >= mLength) {
                    throw new IndexOutOfBoundsException();
                }
                return mSource.charAt(mStart + index);
            }

            public CharSequence subSequence(int start, int end) {
                if (start < 0 || end < 0 || end > mLength || start > end) {
                    throw new IndexOutOfBoundsException();
                }
                return new CharSequenceWrapper(mSource, mStart + start, mStart + end);
            }

            public String toString() {
                return mSource.subSequence(mStart, mEnd).toString();
            }

            public <T> T[] getSpans(int start, int end, Class<T> type) {
                return ((Spanned) mSource).getSpans(mStart + start, mStart + end, type);
            }

            public int getSpanStart(Object tag) {
                return ((Spanned) mSource).getSpanStart(tag) - mStart;
            }

            public int getSpanEnd(Object tag) {
                return ((Spanned) mSource).getSpanEnd(tag) - mStart;
            }

            public int getSpanFlags(Object tag) {
                return ((Spanned) mSource).getSpanFlags(tag);
            }

            public int nextSpanTransition(int start, int limit, Class type) {
                return ((Spanned) mSource).nextSpanTransition(mStart + start, mStart + limit, type)
                        - mStart;
            }
        }
    }

    /**
	 * 该过滤器会限制edits输入的文本长度不能大于指定长度
     * This filter will constrain edits not to make the length of the text
     * greater than the specified length.
     */
    public static class LengthFilter implements InputFilter {
        private final int mMax;

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

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

该接口中主要中一个filter方法,里那个静态内部类,根据案例看如何使用
案例:
输入框只允许输入英文数字中文并且长度为10

        et.setFilters(new InputFilter[]{
                new InputFilter() {
                    @Override
                    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
                        String regex = "[^\u4e00-\u9fa5A-z0-9]";//除中文、数字、英文的其他字符
                        Pattern pattern = Pattern.compile(regex,Pattern.UNICODE_CASE|Pattern.CASE_INSENSITIVE);
                        Matcher matcher = pattern.matcher(source);
                        Log.d("MainActivity","source:"+source+",start:"+start+",end:"+end+",dest:"+dest+",dstart:"+dstart+",dend:"+dend);
                        if(matcher.find()){//如果判断刚输入的字符为非中文、数字、英文得特殊字符就不接受用户的输入内容
                            return "";
                        }
                        return null;
                    }
                },
                //new InputFilter.AllCaps(),//将所有输入的小写转大写,我的案例不需要所以注释了
                new InputFilter.LengthFilter(10)}//需要注意的是,如果代码中setFilters设置了filter,那么xml中maxLength属性就会失效
        );

这里需要注意如果xml中使用maxLength属性,在代码中使用setFilter就会导致maxLength失效,原理很简单,看TextViev的源码中判断如果xml中获取到的maxlength>=0时就会设置一个filter

        if (maxlength >= 0) {
            setFilters(new InputFilter[] { new InputFilter.LengthFilter(maxlength) });
        } else {
            setFilters(NO_FILTERS);
        }

如果在代码中设置filter时就会调用如下代码,这样会将之前的mFilters重新赋值,所以就会导致xml中的maxLength失效

    /**
     * Sets the list of input filters that will be used if the buffer is
     * Editable. Has no effect otherwise.
     *
     * @attr ref android.R.styleable#TextView_maxLength
     */
    public void setFilters(InputFilter[] filters) {
        if (filters == null) {
            throw new IllegalArgumentException();
        }

        mFilters = filters;

        if (mText instanceof Editable) {
            setFilters((Editable) mText, filters);
        }
    }

猜你喜欢

转载自blog.csdn.net/nongminkouhao/article/details/84101467
今日推荐