android - EditText 长度过滤器无法正常工作

android - EditText length filter not working as it should

首先我不得不说我已经在 SO 上阅读过类似的问题和答案,这个问题基本上是 和许多其他问题的重复,但是对这些问题的回答并不像这样我想要。

问题:

像这样以编程方式在我的 EditText 上设置长度过滤器:

editText.setFilters(new InputFilter[]{new LengthFilter(10)} );

它唯一做的就是隐藏超出 EditText 限制的文本。它仍然在建议框中显示长(无限制)文本,我必须删除(退格)每个经过的字母才能删除 EditText 中显示的内容。

建议的解决方案:

  1. 正在将 InputType 设置为 textFilter

    我以编程方式这样做:

    editText.setInputType( InputType.TYPE_TEXT_VARIATION_FILTER );
    

    它隐藏了建议,但无限文本仍然存在,我仍然必须使用退格键删除不应该出现的字母。

  2. 正在将 InputType 设置为 textNoSuggestions|textVisiblePassword

    我以编程方式做了这个(必须添加 TYPE_CLASS_TEXT 否则它不会工作):

    editText.setInputType( InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD );
    

    这个确实有效,但问题是它停止了 "gesture typing" 并将字体更改为等宽字体。

更好的解决方案?
如您所见,如果没有其他问题,这两种方法实际上并不能正常工作。还有其他我错过的方法吗?如果我想保留手势输入和建议,我应该只使用 TextWatcher 吗?

我最终改用了 TextWatcher。我不确定这是否是执行此操作的最佳方法,但它确实适用于建议,并且不会关闭手势输入或更改字体样式。这是我的做法(我对 android 很陌生,所以如果需要改进,请随时告诉我)。

我在评论中添加了一个示例来阐明发生了什么。

创建这些全局变量:

private boolean mWatcherIsBlocked = false;
private String mBeforeChange;
private String mFilteredString; 
private int mCursorPosition = 0;

然后创建 TextWatcher 并将其添加到您的 EditText

final int maxLength = 10; // desired length limit

/** 
 * lets say our EditText is showing "abcdefgh". We select "cdef" from it and 
 * paste a new text "ijklmnop" in the middle. What we should get according to
 * our maxLength is this: 
 * (1) "ab" (0th up to the letter from before_change_text we were selecting) + 
 * (2) "ijklmn" (part of the text we pasted minus the number of letters the whole 
 *      after_change_text goes over the 10 letter limit) + 
 * (3) "gh" (last part of before_change_text that wasn't selected)
 * 
 * so the new text has to be "abijkmngh"
 */

TextWatcher textWatcher = new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        // get before_change_text if textWatcher isn't blocked
        if (!mWatcherIsBlocked) mBeforeChange = s.toString();
    }
    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        if (!mWatcherIsBlocked){
            // get after_change_text if textWatcher isn't blocked
            String after = s.toString();
            // if after_change_text's length is bigger than the limit 
            if (after.length() > maxLength) {
                // see how much it goes over the limit
                int over = after.length() - maxLength;
                // add parts (1) and (2) like our example above
                String st = mBeforeChange.substring(0, start) + // (1)
                            after.substring(start, start + count - over); // (2)
                // get where the cursor position should be after pasting (
                // = after the last letter we could paste = length of (1) + (2) )
                mCursorPosition = st.length();
                // now add part (3) of our text to the first two
                st += mBeforeChange.substring(
                          mBeforeChange.length() - (maxLength - st.length()), 
                          mBeforeChange.length());
                // now assign this new text to a global variable
                mFilteredString = st;
            } else { 
                // if after_change_text hasn't gone over the limit assign it 
                // directly to our global variable
                mFilteredString = s.toString();
            }
        }
    }
    @Override
    public void afterTextChanged(Editable s) {
        // if filtered text is not the same as unfiltered text 
        // or textWatcher is not blocked
        if (!mFilteredString.equals(s.toString()) && !mWatcherIsBlocked) {
            // block textWatcher to avoid infinite loops created by setText 
            // (this might not work as I well as I think!)
            mWatcherIsBlocked = true;
            // set new text to our EditText
            editText.setText(mFilteredString);
            // set its cursor position 
            editText.setSelection(mCursorPosition);
            // unblock the textWatcher
            mWatcherIsBlocked = false;
        }
    }
};

// add the TextWatcher to our EditText
editText.addTextChangedListener(textWatcher);