用TextWatcher限制输入长度并弹出提示

在EditText里面输入,限制输入长度并弹出提示,原本,这是最初级和普遍的需求了,但在细微处却有点小不方便。
 
方法之一,是在xml中添加限制:maxLength="10"。但是,当用户输入达到或超过限制时没有提示。方法之二,给EditText添加TextWatcher监控,在输入超过限制时给予提示,并恢复为之前的状态。试了一下,没法把方法一和方法二结合起来,用maxLength限制后,超过限制时,只是不能输入了,但触发不了TextWatcher,找不到机会触发弹框提示。


给予超出限制的提示很容易,但怎么恢复为本次输入之前状态却需要小心。注意点包括,用户可能键入单个字符,也可能拷贝一个字符串进去,输入光标不一定在字符串尾巴处,也可能在中间或开头。此外,用户可能已经标记高亮了部分字符串。无论哪种,只要输入后会超过限制,那么我们都要恢复成输入之前的状态。
 
使用TextWatcher做输入恢复的步骤是,在TextWatcher的beforeTextChanged方法中保存本次输入之前的字符串及光标位置,接着在afterTextChanged方法中对输入后的字符串长度做检查,如果超过限制,那么在这一步恢复之前的字符串及光标位置。


Android的问题在于,TextWatcher接口中afterTextChanged方法的内部设计比较不友好,存在一个递归嵌套。也即,TextWatcher的正常流程是“beforeTextChanged-->onTextChanged-->afterTextChanged”,但是,当我们在afterTextChanged方法中设法恢复或说修改字符串时,在afterTextChanged方法还没返回的情况下,流程又被触发进入新一个“beforeTextChanged-->onTextChanged-->afterTextChanged”流程,这是因为TextWatcher的设定是,如果字符串被修改,不论在哪,包括我们在第三步afterTextChanged里面修改字符串,都会立即触发并进入新一轮的“beforeTextChanged-->onTextChanged-->afterTextChanged”流程,稍不注意,这里就递归不已,马上耗完内存并stackoverflow了。
 
如下是限制输入长度,并弹出toast的代码,设法避免了嵌套递归:

public class LimitTextWatcher implements TextWatcher {

    public interface IF_callback{
        void callback(int left);
    }

    public IF_callback if_callback;

    EditText editText;
    int maxLength;

    int cursorPositionLast;
    String textLast;
    boolean bypass;

    public LimitTextWatcher(EditText editText, int maxLength, IF_callback if_callback) {

        this.editText = editText;
        this.maxLength = maxLength;
        this.if_callback = if_callback;
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        if (bypass) {

            bypass = false;

        } else {

            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(s);
            textLast = stringBuilder.toString();

            this.cursorPositionLast = editText.getSelectionStart();
        }
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {

    }

    @Override
    public void afterTextChanged(Editable s) {
        if (s.toString().length() > maxLength) {

            int left = maxLength - s.toString().length();

            bypass = true;
            s.clear();

            bypass = true;
            s.append(textLast);

            editText.setSelection(this.cursorPositionLast);

            if (if_callback != null) {
                if_callback.callback(left);
            }
        }

    }

}


edit_text.addTextChangedListener(new LimitTextWatcher(edit_text, MAX_LENGTH, new LimitTextWatcher.IF_callback() {
        @Override
        public void callback(int left) {
            if(left <= 0) {
                Toast.makeText(MainActivity.this, "input is full", Toast.LENGTH_SHORT).show();
            }
        }
    }));

如上代码有个小缺陷:如果在输入之前,用户已经标记高亮了一段子字符串,那么无法恢复这段高亮。android提供设置高亮的头尾光标的方法不起作用,不知为何。

以上的完整代码:https://github.com/maxyou/Limit-EditText



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值