EditText自定义InputFilter完全解析----推荐的监听输入内容的方法

一、简述

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

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

InputFilter

public interface InputFilter 

android.text.InputFilter

Known indirect subclasses

DateKeyListenerDateTimeKeyListenerDialerKeyListenerDigitsKeyListenerInputFilter.AllCapsInputFilter.LengthFilter

LoginFilterLoginFilter.PasswordFilterGMailLoginFilter.UsernameFilterGMailLoginFilter.UsernameFilterGeneric,

NumberKeyListenerTimeKeyListener

 

使用方法

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

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

 

 

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

 

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

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

 

android.text.InputFilter是一个接口,自定义时需要实现方法filter();

import android.text.InputFilter;

public class NumberInputFilter implements InputFilter {

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

        ......

    }
}

 

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空值、有内容字符串)。我翻译的注释里其实已经解释的很清楚了,如果不要理解看了下面的实际输入的例子应该也能理解了。

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值