Android EditText输入限制及字符编码

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

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

实际上在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); 
            } 
        } 
    }

关于编码

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     :光标的结束位置

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

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

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

editText.setFilters(new InputFilter[]{new Utf8ByteLengthFilter(10)});
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;
        }
    }
}

此方法来自与设置-关于手机中设备名称的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);
    }
}

解决方法:

if (keepBytes >= srcByteCount) {
    return null;
} else {
    // 可以加上Toast:输入已达上限
    return "";
}

你可能感兴趣的:(Android,android,edittext,utf-8,unicode)