• Android EditText输入限制及字符编码


    EditText 经常会有限制输入字符长度的需求,限制输入长度的方法有三种:

    1. xml设置maxLength属性
    android:maxLength="50"
    
    • 1
    1. 通过InputFilter过滤长度
    EditText editText = (EditText)findViewById(R.id.edit);
    editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(10)});
    
    • 1
    • 2

    实际上在XML 中设置,最后也是通过设置InputFilter 实现的,LengthFilter 只是实现了字符串长度的限制。

    1. 为EditText设置 TextWatcher 监听

    自定义MyTextWatcher类,实现TextWatcher 接口,监听EditText 的文本变化,手动对输入文本进行截断。

    EditText editText = findViewById(R.id.edit); 
    editText.addTextChangedListener(new MyTextWatcher(editText, 10));
    
    private class MyTextWatcher implements TextWatcher { 
            private EditText editText; 
            private int maxCount; 
     
            MyTextWatcher(EditText editText, int maxCount) { 
                this.editText = editText; 
                this.maxCount = maxCount; 
            } 
     
            @Override 
            public void beforeTextChanged(CharSequence s, int start, int count, int after) { 
     
            } 
     
            @Override 
            public void onTextChanged(CharSequence s, int start, int before, int count) { 
     
            } 
     
            @Override 
            public void afterTextChanged(Editable s) { 
     
                if (s.length() > maxCount) { 
                    editText.setText(s.subSequence(0, maxCount)); 
                    Selection.setSelection(editText.getText(), maxCount); 
                } 
            } 
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    关于编码

    Java编译器默认使用Unicode编码,因此2字节(16位)可以表示所有字符。

    Java基本类型占用的字节数:
    1字节: byte , boolean
    2字节: short , char
    4字节: int , float
    8字节: long , double
    注:1字节(byte)=8位(bits)

    编码与字符:
    通常一个字符相当于一个字节,但是根据编码不同,一个字符也可能等于两个或者三个字符。
    Unicode/GBK: 中文2字节
    UTF-8: 中文通常3字节,在拓展B区之后的是4字节

    InputFilters介绍

    InputFilters用在可编辑的控件,用来限制控件的变化。

    //InputFilter只有一个filter方法
    public CharSequence filter(CharSequence source, int start, int end,Spanned dest, int dstart, int dend);
    
    //filter参数介绍:
    source   :变化的字符串
    start    :变化字符的首字符下标
    end      :变化字符的尾字符下
    dest     :带光标的字符串
    dstart   :光标的起始位置 
    dend     :光标的结束位置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    filter方法返回的是一个CharSequence,用来控制可编辑控件添加字符时的约束条件。主要分为三种情况:

    • 返回null:表示可正常添加source字符串;
    • 返回"":表示不变动原字符;
    • 返回以上之外的字符串:表示将返回的该字符串追加到原字符串中。

    maxLength属性限制的是输入的字符长度,而不是字节长度,如果我们想要限制字符串的字节长度,可以自己实现InputFilter 接口来实现相应功能。通过使用如下工具类,可以实现EditText输入的最大字节长度。

    editText.setFilters(new InputFilter[]{new Utf8ByteLengthFilter(10)});
    
    • 1
    packages/apps/Settings/src/com/android/settings/bluetooth/Utf8ByteLengthFilter.java
    
    public class Utf8ByteLengthFilter implements InputFilter {
        private final int mMaxBytes;
    
        @Keep
        Utf8ByteLengthFilter(int maxBytes) {
            mMaxBytes = maxBytes;
        }
    
        public CharSequence filter(CharSequence source, int start, int end,
                                   Spanned dest, int dstart, int dend) {
            int srcByteCount = 0;
            // count UTF-8 bytes in source substring
            for (int i = start; i < end; i++) {
                char c = source.charAt(i);
                srcByteCount += (c < (char) 0x0080) ? 1 : (c < (char) 0x0800 ? 2 : 3);
            }
            int destLen = dest.length();
            int destByteCount = 0;
            // count UTF-8 bytes in destination excluding replaced section
            for (int i = 0; i < destLen; i++) {
                if (i < dstart || i >= dend) {
                    char c = dest.charAt(i);
                    destByteCount += (c < (char) 0x0080) ? 1 : (c < (char) 0x0800 ? 2 : 3);
                }
            }
            int keepBytes = mMaxBytes - destByteCount;
            if (keepBytes <= 0) {
                return "";
            } else if (keepBytes >= srcByteCount) {
                return null; // use original dest string
            } else {
                // find end position of largest sequence that fits in keepBytes
                for (int i = start; i < end; i++) {
                    char c = source.charAt(i);
                    keepBytes -= (c < (char) 0x0080) ? 1 : (c < (char) 0x0800 ? 2 : 3);
                    if (keepBytes < 0) {
                        return source.subSequence(start, i);
                    }
                }
                // If the entire substring fits, we should have returned null
                // above, so this line should not be reached. If for some
                // reason it is, return null to use the original dest string.
                return null;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    此方法来自与设置-关于手机中设备名称的EditText限制逻辑,原生逻辑是限制30个字节,汉字为3个字节,字母或数字为1个字节,中文标点符号为3个字节,英文标点符号为1个字节,空格为1个字符,即最大输入汉字为10个或英文为30个。但是EditText可输入emoji表情,1个emoj为6个字节,此时当输入9个汉字+1个emoji时,根据Utf8ByteLengthFilter逻辑会过滤掉半个emoji,此时设备名称会失败保存,因为设备名称和蓝牙/WIFI热点名称一致,当 setSsid(@Nullable String ssid)时会checkArgument失败 Preconditions.checkArgument(StandardCharsets.UTF_8.newEncoder().canEncode(ssid)); ,主要问题是此处的逻辑,会拼接上剩余字节数的字符:

    for (int i = start; i < end; i++) {
        char c = source.charAt(i);
        keepBytes -= (c < (char) 0x0080) ? 1 : (c < (char) 0x0800 ? 2 : 3);
        if (keepBytes < 0) {
            return source.subSequence(start, i);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    解决方法:

    if (keepBytes >= srcByteCount) {
        return null;
    } else {
        // 可以加上Toast:输入已达上限
        return "";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    JMeter之脚本录制
    CMS垃圾收集器优缺点
    开源计算机视觉库opencv-python详解
    档案宝档案管理系统在微信小程序上线了!
    基于单片机的声光控制节能灯设计
    LeetCode刷题2:链表篇
    UE5 - ArchvizExplorer - 数字孪生城市模板 -学习笔记
    Java基础练习题(1.以阶乘为例演示方法调用、2.判断一个整数是否为回文数、3.随机生成一个在1-10范围之间的数)
    Win10 蓝屏CRITICAL_PROCESS_DIED值为 0x000000EF
    UVM 覆盖率
  • 原文地址:https://blog.csdn.net/weixin_44008788/article/details/127999097