EditText自定义InputFilter完全解析----推荐

2018-11-20  本文已影响0人  一整块儿腹肌

一、简述

监听EditText的输入是一个十分常见的需求,但是使用setOnEditorActionListener、addTextChangedListener等接口来监听输入不够灵活,而且如果有多种过滤条件,则方法里的逻辑会非常复杂。

推荐使用实现自己输入过滤器来过滤输入内容,使用方便、灵活,而且官方已经实现了许多常用的过滤器。

InputFilter

public interface InputFilter

android.text.InputFilter

Known indirect subclasses

DateKeyListener, DateTimeKeyListener, DialerKeyListener, DigitsKeyListener, InputFilter.AllCaps, InputFilter.LengthFilter, LoginFilter, LoginFilter.PasswordFilterGMail, LoginFilter.UsernameFilterGMail, LoginFilter.UsernameFilterGeneric, NumberKeyListener, TimeKeyListener

使用方法

//通过EditText 的setFilters方法设置,可以传入多个Filter

setFilters(new InputFilter[]{new LengthFilter(maxLength),new CustomFilter()});

二、详细讲解InputFilter API,和如何自定义一个InputFilter

首先这是API的链接(需要翻墙 如果看不到也没关系,下面有详细内容)

https://developer.android.com/reference/android/text/InputFilter

API中的定义

/**
* 当缓冲区要使用source的[start - end)范围内的内容替换dest的[dstart - dend)范围内的内容时调用该方法。
* 返回值是你最终想要添加的文本。如果返回空字符"",则不添加新内容。如果返回空(null),则添加本次输入的全部内容(即source)
* 当你在删除已有文本时,source的长度为0。不要以为是错误而过滤这种清空。
* 不要直接修改dest的内容,它的内容只是用来查看的。
*/
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend);

方法描述:

使用source的下标范围[start--end)的内容,替换dest的下标范围是[dstart-dend)的内容。两个区间都是左闭右开 .end-start是本次输入的字数,dend-dstart是本次被替换的字数。

参数说明:

CharSequence source :本次输入内容

int start :本次输入被选择的起始位置

int end:本次输入被选择的结束位置(不包含)

Spanned dest : 当前输入框中的内容

int dstart :被替换内容在输入框中的起始位置

int dend :被替换内容在输入框中的结束位置(不包含)

返回值说明:

返回值是本次输入最终被输入的内容。有以下三种情况

  1. 返回空字符串:""。 放弃本次输入,即本次输入不会添加到输入框中
  2. **返回空值:null。 **将本次输入完全完全添加到输入框中,相当于return source(不建议使用return source)
  3. **返回指定有内容的字符串:你希望的输入(如"12345")。 **次将指定的字符串添加到输入框中(如"12345")

声明:

被替换内容是指:当选择输入框里的内容,然后输入新的字符,选择的内容将会被新输入的字符替换。这些选择的内容就是被替换的内容。




如果觉得参数不好理解,参考下面的输入实例。

本次输入 b

source = b
start = 0, end =1
dest =
dstart = 0, dend =0

本次输入n

source = n
start = 0, end =1
dest = b
dstart = 1, dend =1

本次输入 m

source = m
start = 0, end =1
dest = bn
dstart = 2, dend =2

本次输入 s

source = s
start = 0, end =1
dest = bnm
dstart = 3, dend =3

本次输入 s

source = s
start = 0, end =1
dest = bnms
dstart = 4, dend =4

本次输入 s

source = s
start = 0, end =1
dest = bnmss
dstart = 5, dend =5

本次输入 孙健

source = 孙健
start = 0, end =2
dest = bnmsss
dstart = 6, dend =6

本次输入 刘德华

source = 刘德华
start = 0, end =3
dest = bnmsss孙健
dstart = 8, dend =8

输入前选中 【刘德华】 然后输入 【张学友****】替换【刘德华】

source = 张学友
start = 0, end =3
dest = bnmsss孙健刘德华
dstart = 8, dend =11

输入前选中 【张学友】**** 然后输入 【黎明****】替换【张学友】

source = 黎明
start = 0, end =2
dest = bnmsss孙健张学友
dstart = 8, dend =11




参数看懂了,我们就来了解一下如何来自定义我们需要的过滤器。

以SDK中已经提供的输入长度过滤器LengthFilter来作为例子,看一下是如何实现限制输入字数的。

/**
 * 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) {
            // keep original   如果剩余可输入字数大于当前正在输入的字数    则返回当前输入   
            //(相当于 return source,不过不建议这么写)
            return null; 
        } 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()方法。


三、定义一个只能输入数字的过滤器

思路:

数字在ASCII中的分为是[48,57],只要在该范围内都是数字。

代码实现:

//通过匿名内部类的方法来实现。如果考虑复用,可以单独声明一个类
InputFilter DigitalFilter = new InputFilter() {
            @Override
            public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {

                if(source.length() <= 0){return "";}

                char [] newChar = new char[source.length()];

                //去掉source中不是数字的字符
                for(int i=source.length();--i>=0;){
                    int chr=source.charAt(i);
                    if(chr > 47 && chr < 58){
                        newChar[i] = (char) chr;
                    }

                }

                //newChar数组中没有赋值的项,在转换为String会被丢弃
                return new String(newChar);
            }
        };



总结:


其实自定义过滤器的难点是理解filter接口的各个参数的意义和返回值表达的含义(api定义注释里的三种情况:""空字符串、null空值、有内容字符串)。我翻译的注释里其实已经解释的很清楚了,如果不要理解看了下面的实际输入的例子应该也能理解了。

只要理解上述内容,根据自己的实际需求返回合适的值即可。

上一篇下一篇

猜你喜欢

热点阅读