EditText 经常会有限制输入字符长度的需求,限制输入长度的方法有三种:
android:maxLength="50"
EditText editText = (EditText)findViewById(R.id.edit);
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(10)});
实际上在XML 中设置,最后也是通过设置InputFilter 实现的,LengthFilter 只是实现了字符串长度
的限制。
自定义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用在可编辑的控件,用来限制控件的变化。
//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,用来控制可编辑控件添加字符时的约束条件。主要分为三种情况:
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 "";
}