android | 限制emoji末尾乱码的字节长度限制过滤器实现

当输入框的长度限制是以UTF-8字节为单位,但又需要支持输入中文、emoji,并且还不能出现emoji乱码?

实现以上需求,你需要知道的事:

  1. 中文字符转UTF-8为3个字节,emoji为4个字节。

  2. emoji由高位代理和低位代理组成,少其中一个都会导致乱码。

  3. InputFilter中的source、start、end、dest、dstart、dend都代表了什么?

理解以上要点之后,就着手代码的编写,方法的核心是:

  1. 先将所有的字符(英文、中文、emoji、其他字符)都转成UTF-8,判断新输入的字符是否满足长度需求。
  2. 若不满足,则需要按照最大允许的长度,截断输入的字符。
  3. 判断截断后的字符是否存在emoji末位乱码情况,有则回退,不允许输入半个emoji
/**
 * 限制emoji末尾乱码的字节长度限制过滤器
 *
 * @param source 输入的文字
 * @param start 输入-0,删除-0
 * @param end 输入-文字的长度,删除-0
 * @param dest 原先显示的内容
 * @param dstart 输入-原光标位置,删除-光标删除结束位置
 * @param dend  输入-原光标位置,删除-光标删除开始位置
 * @return
*/
class LimitCharLengthFilter(private var max: Int) : InputFilter {
    override fun filter(
        source: CharSequence,
        start: Int,
        end: Int,
        dest: Spanned,
        dstart: Int,
        dend: Int
    ): CharSequence {
        GlobalTouchUtil.onGlobalTouch()

        val bytes = dest.toString().toByteArray(StandardCharsets.UTF_8).size
        val sourceBytes = source.toString().toByteArray(StandardCharsets.UTF_8).size

        var keep = 0
        if (bytes + sourceBytes <= max) {
            //输入source之后整体长度不会超过max限制
            keep = source.length
        } else {
            for (i in source.indices) {
                //转成UTF-8
                val currentSourceBytes = source.subSequence(0, i + 1)
                    .toString().toByteArray(StandardCharsets.UTF_8).size

                if (bytes + currentSourceBytes > max) {
                    //超长则截断,记录当前标记,为了不显示后面的字符
                    keep = i
                    //若截断后是半个emoji结尾
                    if (Character.isHighSurrogate(source[ keep ])  ||   Character.isLowSurrogate(source[ keep ])) {
                        //回退这半个emoji,不展示这个emoji,防止乱码
                        --keep
                        if (keep == start) {
                            return ""
                        }
                    }
                    break
                }
            }
        }

        return if (keep <= 0) "" else source.subSequence(start, keep + start)
    }
}

方法调用举例如下:

const val USER_NAME_MAX_LENGTH = 63

//ChineseAndEnglishInputFilter是自定义的限制输入字符范围的filter
//LimitCharLengthFilter(USER_NAME_MAX_LENGTH )是本文介绍的限制emoji末尾乱码的字节长度限制过滤器
etInput.getEditText()?.filters =  listOf(ChineseAndEnglishInputFilter,LimitCharLengthFilter(USER_NAME_MAX_LENGTH ))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值