Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EditText and InputFilter cause repeating text

I'm trying to implement an EditText that limits input to alpha chars only [A-Za-z].

I started with the InputFilter method from this post. When I type "a%" the text disappears then if I hit backspace the text is "a". I've tried other variations on the filter function like using a regex to match only [A-Za-z] and sometimes see crazy behavior like repeating chars, I'll type "a" then "b" and get "aab" then type "c" and get "aabaabc" then hit backspace and get "aabaabcaabaabc"!

Here's the code I'm working with so far with the different approaches I've tried.

    EditText input = (EditText)findViewById( R.id.inputText );
    InputFilter filter = new InputFilter() {
        @Override
        public CharSequence filter( CharSequence source, int start, int end, Spanned dest, int dstart, int dend ) {
            //String data = source.toString();
            //String ret = null;
            /*
            boolean isValid = data.matches( "[A-Za-z]" );
            if( isValid ) {
                ret = null;
            }
            else {
                ret = data.replaceAll( "[@#$%^&*]", "" );
            }
            */
            /*
            dest = new SpannableStringBuilder();
            ret = data.replaceAll( "[@#$%^&*]", "" );
            return ret;
            */

            for( int i = start; i < end; i++ ) {
                if( !Character.isLetter( source.charAt( i ) ) ) {
                    return "";
                }
            }

            return null;
        }
    };
    input.setFilters( new InputFilter[]{ filter } );

I'm totally stumped on this one so any help here would be greatly appreciated.

EDIT: Ok, I've done quite a lot of experimenting with InputFilter and have drawn some conclusions, albeit no solution to the problem. See the comments in my code below. I'm going to try Imran Rana's solution now.

    EditText input = (EditText)findViewById( R.id.inputText );
    InputFilter filter = new InputFilter() {
        // It is not clear what this function should return!
        // Docs say return null to allow the new char(s) and return "" to disallow
        // but the behavior when returning "" is inconsistent.
        // 
        // The source parameter is a SpannableStringBuilder if 1 char is entered but it 
        // equals the whole string from the EditText.
        // If more than one char is entered (as is the case with some keyboards that auto insert 
        // a space after certain chars) then the source param is a CharSequence and equals only 
        // the new chars.
        @Override
        public CharSequence filter( CharSequence source, int start, int end, Spanned dest, int dstart, int dend ) {
            String data = source.toString().substring( start, end );
            String retData = null;

            boolean isValid = data.matches( "[A-Za-z]+" );
            if( !isValid ) {
                if( source instanceof SpannableStringBuilder ) {
                    // This works until the next char is evaluated then you get repeats 
                    // (Enter "a" then "^" gives "a". Then enter "b" gives "aab")
                    retData = data.replaceAll( "[@#$%^&*']", "" );
                    // If I instead always returns an empty string here then the EditText is blanked.
                    // (Enter "a" then "^" gives "")
                    //retData = "";
                }
                else { // source is instanceof CharSequence
                    // We only get here if more than 1 char was entered (like "& ").
                    // And again, this works until the next char is evaluated then you get repeats 
                    // (Enter "a" then "& " gives "a". Then enter "b" gives "aab")
                    retData = "";
                }
            }

            return retData;
        }
    };
    input.setFilters( new InputFilter[]{ filter } );
like image 453
Rooster242 Avatar asked May 29 '12 01:05

Rooster242


People also ask

What is the difference between an EditText and a TextView?

EditText is used for user input. TextView is used to display text and is not editable by the user. TextView can be updated programatically at any time.

How do I know if EditText is focused?

You can use View. OnFocusChangeListener to detect if any view (edittext) gained or lost focus. This goes in your activity or fragment or wherever you have the EditTexts.

What is the use of input filter in android?

The InputFilter Interface has one method, filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) , and it provides you with all the information you need to know about which characters were entered into the EditText it is assigned to.

What is the function of setting autoText attribute in EditText?

android:autoText If set, specifies that this TextView has a textual input method and automatically corrects some common spelling errors.


2 Answers

Use the following code:

EditText input = (EditText) findViewById(R.id.inputText);
   input.addTextChangedListener(new TextWatcher() {

    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // TODO Auto-generated method stub
         for( int i = start;i<s.toString().length(); i++ ) {
             if( !Character.isLetter(s.charAt( i ) ) ) {
                input.setText("");
             }
         }

    }

    public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {
        // TODO Auto-generated method stub

    }

    public void afterTextChanged(Editable s) {
        // TODO Auto-generated method stub

    }
   });

If you want the valid text to remain in the EditText:


 input.addTextChangedListener(new TextWatcher() {

    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // TODO Auto-generated method stub

    }

    public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {
        // TODO Auto-generated method stub

    }
    public void afterTextChanged(Editable s) {
        // TODO Auto-generated method stub
         for( int i = 0;i<s.toString().length(); i++ ) {
             if( !Character.isLetter(s.charAt( i ) ) ) {                    
                s.replace(i, i+1,"");               
             }
         }
    }
   });
like image 143
Imran Rana Avatar answered Sep 23 '22 03:09

Imran Rana


Fix for repeating text, work on all Android Versions:

public static InputFilter getOnlyCharactersFilter() {
    return getCustomInputFilter(true, false, false);
}

public static InputFilter getCharactersAndDigitsFilter() {
    return getCustomInputFilter(true, true, false);
}

public static InputFilter getCustomInputFilter(final boolean allowCharacters, final boolean allowDigits, final boolean allowSpaceChar) {
    return new InputFilter() {
        @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)) {
                    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, sb.length(), null, sp, 0);
                    return sp;
                } else {
                    return sb;
                }
            }
        }

        private boolean isCharAllowed(char c) {
            if (Character.isLetter(c) && allowCharacters) {
                return true;
            }
            if (Character.isDigit(c) && allowDigits) {
                return true;
            }
            if (Character.isSpaceChar(c) && allowSpaceChar) {
                return true;
            }
            return false;
        }
    };
}

Now you can use this filer like:

 //Accept Characters Only
edit_text.setFilters(new InputFilter[]{getOnlyCharactersFilter()});

//Accept Digits and Characters
edit_text.setFilters(new InputFilter[]{getCharactersAndDigitsFilter()});

//Accept Digits and Characters and SpaceBar
edit_text.setFilters(new InputFilter[]{getCustomInputFilter(true,true,true)});
like image 31
Salam El-Banna Avatar answered Sep 19 '22 03:09

Salam El-Banna