Last active
March 30, 2023 09:51
-
-
Save maoruibin/0c7bca8d024c0e5718fa8b029850bca0 to your computer and use it in GitHub Desktop.
EditText View 限制输入字符数,并倒计还能输入多少字符,兼容中英文字符
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* EditText View 限制输入字符数,并倒计还能输入多少字符,兼容中英文字符 | |
* author : ruibin1 ([email protected]) | |
* create : 2019/3/28 - 2:12 PM. | |
* thanks : https://github.com/FJ917/FJEditTextCount | |
*/ | |
public class EditTextCountWatch { | |
private EditText mEtContent; | |
private TextView mTvNum; | |
//最大字符 | |
private int mMaxNum = 10; | |
//支持输入的最多字符数量,如支持最多 10 个 | |
private int mMaxLenChar = mMaxNum * 2; | |
private int mStartCountNum = 0; | |
private Callback mCallback; | |
private InputFilter filter = new InputFilter() { | |
@Override | |
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { | |
int dindex = 0; | |
int count = 0; | |
while (count <= mMaxLenChar && dindex < dest.length()) { | |
char c = dest.charAt(dindex++); | |
if (c < 128) { | |
count = count + 1; | |
} else { | |
count = count + 2; | |
} | |
} | |
if (count > mMaxLenChar) { | |
return dest.subSequence(0, dindex - 1); | |
} | |
int sindex = 0; | |
while (count <= mMaxLenChar && sindex < source.length()) { | |
char c = source.charAt(sindex++); | |
if (c < 128) { | |
count = count + 1; | |
} else { | |
count = count + 2; | |
} | |
} | |
if (count > mMaxLenChar) { | |
sindex--; | |
} | |
return source.subSequence(0, sindex); | |
} | |
}; | |
/** | |
* | |
* @param etContent 输入 View | |
*/ | |
public EditTextCountWatch(EditText etContent) { | |
this.mEtContent = etContent; | |
} | |
/** | |
* 倒计数显示对应的 view | |
* @param tvNumCount | |
* @return | |
*/ | |
public EditTextCountWatch numCountView(TextView tvNumCount){ | |
this.mTvNum = tvNumCount; | |
return this; | |
} | |
public EditTextCountWatch callback(Callback callback){ | |
this.mCallback = callback; | |
return this; | |
} | |
/** | |
* 设置最大输入的字符数,不考虑中英文 | |
* @param maxNum | |
* @return | |
*/ | |
public EditTextCountWatch maxNum(int maxNum){ | |
this.mMaxNum = maxNum; | |
this.mMaxLenChar = maxNum*2; | |
return this; | |
} | |
/** | |
* 开始计数的字符数,比如可以输入 30 个字符,但是一开始并不倒计数,直到 输入 20 个才开始计数 | |
* @param startCountNum | |
* @return | |
*/ | |
public EditTextCountWatch startCountNum(int startCountNum){ | |
this.mStartCountNum = startCountNum * 2; | |
return this; | |
} | |
public void build(){ | |
if(mTvNum == null){ | |
throw new IllegalStateException("tvNumCount view can not be null"); | |
} | |
//设置长度 | |
mEtContent.setFilters(new InputFilter[]{filter}); | |
//监听输入 | |
mEtContent.addTextChangedListener(mTextWatcher); | |
} | |
/** 刷新剩余输入字数 */ | |
private void setLeftCount() { | |
//到目标位置才开始计数 | |
long inputCount = getInputCount(); | |
if (mStartCountNum > inputCount) { | |
mTvNum.setText(""); | |
}else{ | |
mTvNum.setTextColor(Color.parseColor("#cccccc")); | |
int leftNum = mMaxNum - calculateTextCharacterLength(mEtContent.getText().toString()); | |
mTvNum.setText(String.valueOf(leftNum)); | |
if (leftNum <= 0) { | |
mTvNum.setTextColor(Color.parseColor("#F31212")); | |
} | |
} | |
} | |
/** | |
* 计算输入文字长度 | |
* 引用自前版本的计算方式 | |
* @param text | |
* @return 返回文字长度的字节数 如果最终要计算字符数需要/2取整 | |
*/ | |
private static int calculateTextLength(String text) { | |
if(TextUtils.isEmpty(text)){ | |
return 0; | |
} | |
int totle = 0; | |
boolean isEmptyText = true; | |
char ch; | |
for (int i = 0, len = text.length(); i < len; i++) { | |
ch = (char) text.codePointAt(i); | |
if (isEmptyText && ch != ' ' && ch != '\n') { // 文本含有不是空格和回车的字符 | |
isEmptyText = false; | |
} | |
totle += ch > 255 ? 2 : 1; | |
} | |
// 文本只含有空格和回车 | |
if (isEmptyText) { | |
return 0; | |
} | |
return totle; | |
} | |
/** 获取用户输入内容字数 */ | |
private long getInputCount() { | |
return calculateTextLength(mEtContent.getText().toString()); | |
} | |
private TextWatcher mTextWatcher = new TextWatcher() { | |
private int editStart; | |
private int editEnd; | |
public void afterTextChanged(Editable s) { | |
editStart = mEtContent.getSelectionStart(); | |
editEnd = mEtContent.getSelectionEnd(); | |
// 先去掉监听器,否则会出现栈溢出 | |
mEtContent.removeTextChangedListener(mTextWatcher); | |
// 注意这里只能每次都对整个EditText的内容求长度,不能对删除的单个字符求长度 | |
// 因为是中英文混合,单个字符而言,calculateLength函数都会返回1 | |
while (mMaxNum - calculateTextCharacterLength(s.toString()) < 0 ) { // 当输入字符个数超过限制的大小时,进行截断操作 | |
s.delete(editStart - 1, editEnd); | |
editStart--; | |
editEnd--; | |
} | |
// 恢复监听器 | |
mEtContent.addTextChangedListener(mTextWatcher); | |
setLeftCount(); | |
if(mCallback != null){ | |
mCallback.onTextChanged(mEtContent.getText().toString()); | |
} | |
} | |
public void beforeTextChanged(CharSequence s, int start, int count,int after) {} | |
public void onTextChanged(CharSequence s, int start, int before,int count) {} | |
}; | |
private static int calculateTextCharacterLength(String text) { | |
int byteLength = calculateTextLength(text); | |
return (int)Math.ceil(byteLength/2.0); | |
} | |
public interface Callback{ | |
void onTextChanged(String content); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment