InputFilter on EditText cause repeating text

99封情书 提交于 2019-12-03 23:28:31

The problem of characters duplication comes from InputFilter bad implementation. Rather return null if replacement should not change:

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
    boolean keepOriginal = true;
    StringBuilder sb = new StringBuilder(end - start);
    for (int i = start; i < end; i++) {
        char c = source.charAt(i);
        if (isCharAllowed(c)) // put your condition here
            sb.append(c);
        else
            keepOriginal = false;
    }
    if (keepOriginal)
        return null;
    else {
        if (source instanceof Spanned) {
            SpannableString sp = new SpannableString(sb);
            TextUtils.copySpansFrom((Spanned) source, start, end, null, sp, 0);
            return sp;
        } else {
            return sb;
        }           
    }
}

private boolean isCharAllowed(char c) {
    return Character.isUpperCase(c) || Character.isDigit(c);
}

I have found many bugs in the Android's InputFilter, I am not sure if those are bugs or intended to be so. But definitely it did not meet my requirements. So I chose to use TextWatcher instead of InputFilter

private String newStr = "";

myEditText.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            // Do nothing
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            String str = s.toString();
            if (str.isEmpty()) {
                myEditText.append(newStr);
                newStr = "";
            } else if (!str.equals(newStr)) {
                // Replace the regex as per requirement
                newStr = str.replaceAll("[^A-Z0-9]", "");
                myEditText.setText("");
            }
        }

        @Override
        public void afterTextChanged(Editable s) {
            // Do nothing
        }
    });

The above code does not allow users to type any special symbol into your EditText. Only capital alphanumeric characters are allowed.

InputFilters can be attached to Editable S to constrain the changes that can be made to them. Refer that it emphasises on changes made rather than whole text it contains..

Follow as mentioned below...

 public class DemoFilter implements InputFilter {

        public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart,
                int dend) {

            if (source.equals("")) { // for backspace
                return source;
            }
            if (source.toString().matches("[a-zA-Z0-9 ]*")) // put your constraints
                                                            // here
            {
               char[] ch = new char[end - start];

              TextUtils.getChars(source, start, end, ch, 0);

                // make the characters uppercase
                String retChar = new String(ch).toUpperCase();
                return retChar;
            }
            return "";
        }
    }

I've run into the same issue, after fixing it with solutions posted here there was still a remaining issue with keyboards with autocomplete. One solution is to set the inputType as 'visiblePassword' but that's reducing functionality isn't it?

I was able to fix the solution by, when returning a non-null result in the filter() method, use the call

TextUtils.copySpansFrom((Spanned) source, start, newString.length(), null, newString, 0);

This copies the auto-complete spans into the new result and fixes the weird behaviour of repetition when selecting autocomplete suggestions.

Same for me, InputFilter duplicates characters. This is what I've used:

Kotlin version:

private fun replaceInvalidCharacters(value: String) = value.replace("[a-zA-Z0-9 ]*".toRegex(), "")

textView.addTextChangedListener(object : TextWatcher {
    override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}

    override fun afterTextChanged(s: Editable) {}

    override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
        val newValue = replaceInvalidCharacters(s.toString())
        if (newValue != s.toString()) {
            textView.setText(newValue)
            textView.setSelection(textView.text.length)
        }
    }
})

works well.

try this:

class CustomInputFilter implements InputFilter {
    StringBuilder sb = new StringBuilder();

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        Log.d(TAG, "filter " + source + " " + start + " " + end + " dest " + dest + " " + dstart + " " + dend);
        sb.setLength(0);
        for (int i = start; i < end; i++) {
            char c = source.charAt(i);
            if (Character.isUpperCase(c) || Character.isDigit(c) || c == ' ') {
                sb.append(c);
            } else
            if (Character.isLowerCase(c)) {
                sb.append(Character.toUpperCase(c));
            }
        }
        return sb;
    }
}

this also allows filtering when filter() method accepts multiple characters at once e.g. pasted text from a clipboard

The following solution also supports the option of an autocomplete keyboard

editTextFreeNote.addTextChangedListener( new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            String newStr = s.toString();
            newStr = newStr.replaceAll( "[a-zA-Z0-9 ]*", "" );
            if(!s.toString().equals( newStr )) {
                editTextFreeNote.setText( newStr );
                editTextFreeNote.setSelection(editTextFreeNote.getText().length());
            }
        }

        @Override
        public void afterTextChanged(Editable s) {}
    } );

I've met this problem few times before. Setting some kinds of inputTypes in xml propably is the source of problem. To resolve it without any additional logic in InputFilter or TextWatcher just set input type in code instead xml like this:

editText.setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);

recently i faced same problem reason of the problem is... if there is a no change in the input string then don't return source string return null, some device doesn't handle this properly that's why characters are repating.

in your code you are returning

return source.toString().toUpperCase();

don't return this , return null; in place of return source.toString().toUpperCase(); , but it will be a patch fix , it will not handle all scenarios , for all scenario you can use this code.

public class SpecialCharacterInputFilter implements InputFilter {

    private static final String PATTERN = "[^A-Za-z0-9]";
    // if you want to allow space use this pattern
    //private static final String PATTERN = "[^A-Za-z\\s]";

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        // Only keep characters that are letters and digits
        String str = source.toString();
        str = str.replaceAll(PATTERN, AppConstants.EMPTY_STRING);
        return str.length() == source.length() ? null : str;
    }
}

what is happening in this code , there is a regular expression by this we will find all characters except alphabets and digits , now it will replace all characters with empty string, then remaining string will have alphabets and digits.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!