Skip to content

Instantly share code, notes, and snippets.

@maoruibin
Last active March 30, 2023 09:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maoruibin/0c7bca8d024c0e5718fa8b029850bca0 to your computer and use it in GitHub Desktop.
Save maoruibin/0c7bca8d024c0e5718fa8b029850bca0 to your computer and use it in GitHub Desktop.
EditText View 限制输入字符数,并倒计还能输入多少字符,兼容中英文字符
/**
* EditText View 限制输入字符数,并倒计还能输入多少字符,兼容中英文字符
* author : ruibin1 (ruibin1@staff.weibo.com)
* 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