让 Android 输入框只能输入固定长度的中英文

很多 App 都会要求输入的字符种类、长度有所限制,在此之前我其实已经遇到了这样的需求,只能输入中文和英文,并且不同的语言所限制的长度不同。那个时候会觉得这样的限制比较麻烦,因为总认为涉及到中文的判断就比较麻烦,所以推脱说实现比较麻烦没有太多的时间,就给暂时压了下来。直到第二次遇到这样的需求我直到没办法再退了,结果 google 了下发现判断中文也没那么麻烦,使用正则判断中文字符范围即可。

另外类似需求的实现逻辑也可能不同,比如有的是中文、英文分别限制,中文可以输入 7 字符,英文可以输入 14 字符,但是只要有 1 个中文字就按照中文的长度限制,这样你输入了 1 个中文字之后就只能输入 6 个英文字符了,感觉并不合理。因此我的实现逻辑是:1 个中文字符算两个英文字符,然后计算总长度(在 Android 里不论中英文都默认计算为 1 个字符)。这样输入 1 个中文字后还能继续输入 12 个英文字。

一般简单的限制输入长度只需要设置 maxLength 即可,但是这种复合的需求,就只能通过 InputFilter 来实现了。InputFilter 是一个接口,并且 Android 官方提供很多种默认实现,有的很有意思,这里不一一列举,感兴趣的可以自己去看看。

InputFilter 只有一个方法:

    public CharSequence filter(CharSequence source, int start, int end,
                               Spanned dest, int dstart, int dend);
复制代码

source 代表新输入待验证过滤的字符串,start 和 end 是新输入字符串的起始; dest 是被验证通过已经输入的字符,dstart 和 dend 是已经输入文本的起始; 如果是输入状态,那么 dstart == dend,但是 start < end; 如果是删除状态,那么 start == end,但是 dstart < dend ,并且两者之差就是删除字符的长度。

这个方法所返回值的不同所表示的意义也不同:

  • 返回 null 表示不做过滤和限制;
  • 返回 “” 表示全部过滤,不能输入任何字符;
  • 返回非空字符串表示过滤通过字符串的内容,当然字符串的内容我们完全可以自定义;

有了以上认识可以更好的完成目标:

  1. 判断新输入的字符是中文还是英文(目前来看还没有能够同时输入包含中英文的情况);
  2. 对中英文按不同长度分别计算总长度,去掉超出的部分;
  3. 返回对应的值

实现如下:

class SizeFilterWithTextAndLetter(private var letterMaxLength: Int) : InputFilter {
    private val chinesePattern = Pattern.compile("[\u4e00-\u9fa5]+")
    private val letterPattern = Pattern.compile("[a-zA-Z]+")

    override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? {
        val isAdd = dstart == dend
        if (!isAdd) {//删除文字不限制
            return null
        }
        val originLength = calculateLength(dest)

        val isChinese = chinesePattern.matcher(source.toString()).find()
        val isLetter = letterPattern.matcher(source.toString()).find()
        if (isChinese) {
            return when {
                originLength >= letterMaxLength -> ""
                originLength + source.length * 2 > letterMaxLength -> {
                    var length = (letterMaxLength - originLength + 1) / 2
                    if (length < 0) length = 0
                    source.subSequence(0, length)
                }
                else -> null
            }
        } else if (isLetter) {
            return when {
                originLength >= letterMaxLength -> ""
                originLength + source.length > letterMaxLength -> {
                    letterMaxLength = source.length
                    val length = letterMaxLength
                    source.subSequence(0, length)
                }
                else -> null
            }
        }

        return ""
    }

    /**
     * 计算原有文本占据的长度
     *
     * @param dest 原文本
     * @return 总长度
     */
    private fun calculateLength(dest: Spanned): Int {
        var chineseLength = 0
        var letterLength = 0
        for (i in 0 until dest.length) {
            if (chinesePattern.matcher(dest.subSequence(i, i + 1)).find()) {
                chineseLength += 2
            }
            if (letterPattern.matcher(dest.subSequence(i, i + 1)).find()) {
                letterLength += 1
            }
        }
        return chineseLength + letterLength
    }
}
复制代码

使用方法:

//过滤器是以数组的形式设置,此处限制输入 14 英文字符,也就是 7 中文字符
edit_value?.filters = arrayOf(SizeFilterWithTextAndLetter(14))
复制代码

本文最早发表在 alphagao.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值