How do I use InputFilter to limit characters in an EditText in Android?
Asked Answered
C

20

194

I want to restrict the chars to 0-9, a-z, A-Z and spacebar only. Setting inputtype I can limit to digits but I cannot figure out the ways of Inputfilter looking through the docs.

Cami answered 28/7, 2010 at 0:49 Comment(0)
T
195

I found this on another forum. Works like a champ.

InputFilter filter = new InputFilter() {
    public CharSequence filter(CharSequence source, int start, int end,
            Spanned dest, int dstart, int dend) {
        for (int i = start; i < end; i++) {
            if (!Character.isLetterOrDigit(source.charAt(i))) {
                return "";
            }
        }
        return null;
    }
};
edit.setFilters(new InputFilter[] { filter });
Tavie answered 9/12, 2010 at 17:26 Comment(5)
actually it doesn't work as well in newer Androids (like 4.0+). They introduce dictionary suggestions above the keyboard. When you type a common word (let's say "the") followed by an illegal character for this filter (say, "-"), the whole word is deleted and after you type another characters (even allowed ones, like "blah") filter returns "" and no character shows up in the field. This is because the method gets a SpannableStringBuilder in source parameter with "the-blah" in it and start/end parameters spanning the whole input string... See my answer for a better solution.Bo
In that example, where it returns "", I think it should return the text that should be displayed. i.e. you should remove the illegal characters and return the string you WANT displayed. developer.android.com/reference/android/widget/…, android.view.KeyEvent)Hardpan
@AndrewMackenzie If the input character, for example, a comma-',', which isn't legal, and I want to remove it, how to fixed the code above.Bootblack
!Character.isLetterOrDigit(source.charAt(i)) && !Character.isSpaceChar(source.charAt(i)) to allow for spacesPneumodynamics
@ŁukaszSromek In newer android, especially samsung devices, you can disable text autocomplete(if suitable for your needs) with a line in your xml file. You can try android:inputType="textVisiblePassword" to disable the autocomplete that is the real problem. It would not be the proper way, but it just works. Cheers!Littlefield
A
150

InputFilters are a little complicated in Android versions that display dictionary suggestions. You sometimes get a SpannableStringBuilder, sometimes a plain String in the source parameter.

The following InputFilter should work. Feel free to improve this code!

new InputFilter() {
    @Override
    public CharSequence filter(CharSequence source, int start, int end,
            Spanned dest, int dstart, int dend) {

        if (source instanceof SpannableStringBuilder) {
            SpannableStringBuilder sourceAsSpannableBuilder = (SpannableStringBuilder)source;
            for (int i = end - 1; i >= start; i--) { 
                char currentChar = source.charAt(i);
                 if (!Character.isLetterOrDigit(currentChar) && !Character.isSpaceChar(currentChar)) {    
                     sourceAsSpannableBuilder.delete(i, i+1);
                 }     
            }
            return source;
        } else {
            StringBuilder filteredStringBuilder = new StringBuilder();
            for (int i = start; i < end; i++) { 
                char currentChar = source.charAt(i);
                if (Character.isLetterOrDigit(currentChar) || Character.isSpaceChar(currentChar)) {    
                    filteredStringBuilder.append(currentChar);
                }     
            }
            return filteredStringBuilder.toString();
        }
    }
}
Aground answered 29/9, 2012 at 20:20 Comment(9)
is there a reason why you wouldn't want to subsequence the source? Do you see anything wrong with just doing this (in order to only allow alphanumerics plus a few special characters): String replacement = source.subSequence(start, end).toString(); return replacement.replaceAll("[^A-Za-z0-9_\\-@]", "");Kazachok
This does not take into account an issue where repeating dictionary suggestion text shows up. @serwus identified that in their answer. Basically you should return null if no modifications are made in both cases.Aundreaaunson
This code work perfect as lukasz said(in above answer) but i'm facing some problem with the non-dictionary words. when i type chiru it shows 3times like chiruchiruchiru. how to solve it? and its takes white spaces too but how to restrict next to next white spaces?Feverfew
For some reason, when source instanceof SpannableStringBuilder, entering AB gives me AAB like when trying the previous answer. Luckily I was able to work around it by using @florian solution below.Autocade
@Aundreaaunson is absolutely right. While it does manage to get the string right eventually, it messes up when you use dictionary/word completion.Lengthen
Is there any way to make an inputFilter remove characters specified using a Regex?Bunco
I agree with @Lukasz, the InputFilters have a weird behaviour with the dicctionaries........This answer is perfect, and this case is in the official documentation developer.android.com/reference/android/text/…, int, int, android.text.Spanned, int, int), If source is an instance of Spanned or Spannable, the span objects in the source should be copied into the filtered result (i.e. the non-null return value). copySpansFrom(Spanned, int, int, Class, Spannable, int) can be used for convenience.Ebsen
This is not perfect either and screws up with the backspace key.Orr
It is not perfect even without dictionaries: When you type a filtered character immediately following a valid space, then instead of its being filtered, the invalid character is replaced with an additional space.Gaynellegayner
U
109

much easier:

<EditText
    android:inputType="text"
    android:digits="0,1,2,3,4,5,6,7,8,9,*,qwertzuiopasdfghjklyxcvbnm" />
Upchurch answered 14/12, 2012 at 7:13 Comment(6)
While this seems like a perfect answer there is an issue according to the docs: "As for all implementations of KeyListener, this class is only concerned with hardware keyboards. Software input methods have no obligation to trigger the methods in this class." I think an InputFilter is probably a better, albeit more complicated, way to go.Eyetooth
Awesome Solution Just Want to add You need not to give "," in between. You can use something like this "0123456789qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM"Hayleyhayloft
Not a multilingual solutionVoss
If you plan on using translations in your app. DO NOT USE THIS SOLUTION!Nocti
Seems like this may not work with imeOptions="actionNext", etc.Whin
@grantespo, how does it depend on translation, if we want to enter English letters and digits?Groats
B
78

None of posted answers did work for me. I came with my own solution:

InputFilter filter = 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)) // 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, sb.length(), null, sp, 0);
                return sp;
            } else {
                return sb;
            }           
        }
    }

    private boolean isCharAllowed(char c) {
        return Character.isLetterOrDigit(c) || Character.isSpaceChar(c);
    }
}
editText.setFilters(new InputFilter[] { filter });
Blynn answered 2/12, 2013 at 10:21 Comment(9)
This is the only answer that actually has the right approach to prevent repeating text from dictionary suggestions! Upvote!Aundreaaunson
The only thing, EditText can already have its own filters, e.g. length filter. So instead of just overriding the filters, you most likely wish to add your filters to already existing ones.Gwinn
Is this still up to date? For me Android 6.0.1 it works on onscreen keyboardTifanytiff
How can I set a maxLength?Tifanytiff
@Tifanytiff add a LengthFilterAssignment
A minor issue with this answer (or with how Android's input mechanism works) is that backspace sometimes appears to the user to not be working because they are backspacing over previously entered invalid characters that are still held in the source buffer.Assignment
Kudos for extracting isCharAllowed, this is good programming.Gaynellegayner
Same problem as with Łukasz Sromek's solution: An invalid character following a space is replaced by a space.Gaynellegayner
It's not working with fast whitespace inputManagerial
W
30

Use this its work 100% your need and very simple.

<EditText
android:inputType="textFilter"
android:digits="@string/myAlphaNumeric" />

In strings.xml

<string name="myAlphaNumeric">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</string>
Winola answered 4/11, 2014 at 13:2 Comment(2)
I believe you're missing the space in here, but of course you simply need to add a space to "myAlphaNumeric" to make it work.Bost
What about cyrillic chars?Managerial
H
19

I have done something like this to keep it simple:

edit_text.filters = arrayOf(object : InputFilter {
    override fun filter(
        source: CharSequence?,
        start: Int,
        end: Int,
        dest: Spanned?,
        dstart: Int,
        dend: Int
    ): CharSequence? {
        return source?.subSequence(start, end)
            ?.replace(Regex("[^A-Za-z0-9 ]"), "")
    }
})

This way we are replacing all the unwanted characters in the new part of the source string with an empty string.

The edit_text variable is the EditText object we are referring to.

The code is written in kotlin.

Helot answered 19/5, 2020 at 12:12 Comment(4)
Thanks! This solution works well when typing as well as pasting a text.Groats
This should be the accepted answer! Doing the TextWatcher will occurred StackOverFlowError, unless do the following checker as shown in here: link.medium.com/YfjQwprHUhbIrruptive
Amazing solution. Specially works great when pasting a number from dialer to EditTextCornela
Notice that InputFilter may be incompatible with TextWatcher. In case it doesn't work as expected (clears text) check that you didn't add TextWatcher. InputFilter will reset maxLength. To enable it, use edit_text.filters = arrayOf(*aboveInputFilter*, InputFilter.LengthFilter(10)).Groats
S
18

To avoid Special Characters in input type

public static InputFilter filter = new InputFilter() {
    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        String blockCharacterSet = "~#^|$%*!@/()-'\":;,?{}=!$^';,?×÷<>{}€£¥₩%~`¤♡♥_|《》¡¿°•○●□■◇◆♧♣▲▼▶◀↑↓←→☆★▪:-);-):-D:-(:'(:O 1234567890";
        if (source != null && blockCharacterSet.contains(("" + source))) {
            return "";
        }
        return null;
    }
};

You can set filter to your edit text like below

edtText.setFilters(new InputFilter[] { filter });
Stevestevedore answered 13/3, 2015 at 5:23 Comment(1)
Doesn't block any of these characters. ㋡ ㋛ ☺ ☹ ☻ 〠 シ ッ ツ ヅ Ü 〲 〴 ϡ ﭢ ت ⍡ ⍢ ⍣ ⍤ ⍥ ⍨ ⍩ ὃ ὕ ὣ ѶCongratulatory
G
8

First add into strings.xml:

<string name="vin_code_mask">0123456789abcdefghjklmnprstuvwxyz</string>

XML:

android:digits="@string/vin_code_mask"

Code in Kotlin:

edit_text.filters += InputFilter { source, start, end, _, _, _ ->
    val mask = getString(R.string.vin_code_mask)
    for (i in start until end) {
        if (!mask.contains(source[i])) {
            return@InputFilter ""
        }
    }
    null
}

Strange, but it works weirdly on emulator's soft keyboard.

Warning! The following code will filter all letters and other symbols except digits for software keyboards. Only digital keyboard will appear on smartphones.

edit_text.keyListener = DigitsKeyListener.getInstance(context.getString(R.string.vin_code_mask))

I also usually set maxLength, filters, inputType.

Groats answered 10/12, 2019 at 11:5 Comment(0)
T
7

In addition to the accepted answer, it is also possible to use e.g.: android:inputType="textCapCharacters" as an attribute of <EditText> in order to only accept upper case characters (and numbers).

Tumbledown answered 16/1, 2011 at 12:37 Comment(2)
android:inputType="textCapCharacters" doesnot restrict to usage of other chars like '., -" etc.Awning
It also is only a hint to the input method. It does not restrict what characters are allowed to be input.Sudan
F
7

It's Right, the best way to go about it to fix it in the XML Layout itself using:

<EditText
android:inputType="text"
android:digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" />

as rightly pointed by Florian Fröhlich, it works well for text views even.

<TextView
android:inputType="text"
android:digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" />

Just a word of caution, the characters mentioned in the android:digits will only be displayed, so just be careful not to miss any set of characters out :)

Flowage answered 23/4, 2014 at 9:23 Comment(2)
you need to define inputType="textFilter" then only it will going to work proper.Ulna
@ShreyashMahajan, does it depend on device / keyboard app? In my case inputType doesn't affect filtering.Groats
D
7

You can specify wanted characters in a regex and use it in InputFilter:

val regex = Regex("[a-zA-Z\\d ]")
    
editText.filters = arrayOf(InputFilter { source, _, _, _, _, _ ->
    source.filter { regex.matches(it.toString()) }
})

Notice, I didn't use \w character class, because it includes underscore _

Donell answered 30/10, 2020 at 15:57 Comment(0)
T
5

For some reason the android.text.LoginFilter class's constructor is package-scoped, so you can't directly extend it (even though it would be identical to this code). But you can extend LoginFilter.UsernameFilterGeneric! Then you just have this:

class ABCFilter extends LoginFilter.UsernameFilterGeneric {
    public UsernameFilter() {
        super(false); // false prevents not-allowed characters from being appended
    }

    @Override
    public boolean isAllowed(char c) {
        if ('A' <= c && c <= 'C')
            return true;
        if ('a' <= c && c <= 'c')
            return true;

        return false;
    }
}

This isn't really documented, but it's part of the core lib, and the source is straightforward. I've been using it for a while now, so far no problems, though I admit I haven't tried doing anything complex involving spannables.

Thies answered 2/10, 2013 at 1:20 Comment(0)
H
4

This simple solution worked for me when I needed to prevent the user from entering empty strings into an EditText. You can of course add more characters:

InputFilter textFilter = new InputFilter() {

@Override

public CharSequence filter(CharSequence c, int arg1, int arg2,

    Spanned arg3, int arg4, int arg5) {

    StringBuilder sbText = new StringBuilder(c);

    String text = sbText.toString();

    if (text.contains(" ")) {    
        return "";   
    }    
    return c;   
    }   
};

private void setTextFilter(EditText editText) {

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

}
Havre answered 19/10, 2013 at 4:56 Comment(1)
how to call this solution?Firkin
S
2

This is an old thread, but the purposed solutions all have issues (depending on device / Android version / Keyboard).

DIFFERENT APPROACH

So eventually I went with a different approach, instead of using the InputFilter problematic implementation, I am using TextWatcher and the TextChangedListener of the EditText.

FULL CODE (EXAMPLE)

editText.addTextChangedListener(new TextWatcher() {

    @Override
    public void afterTextChanged(Editable editable) {
        super.afterTextChanged(editable);

        String originalText = editable.toString();
        int originalTextLength = originalText.length();
        int currentSelection = editText.getSelectionStart();

        // Create the filtered text
        StringBuilder sb = new StringBuilder();
        boolean hasChanged = false;
        for (int i = 0; i < originalTextLength; i++) {
            char currentChar = originalText.charAt(i);
            if (isAllowed(currentChar)) {
                sb.append(currentChar);
            } else {
                hasChanged = true;
                if (currentSelection >= i) {
                    currentSelection--;
                }
            }
        }

        // If we filtered something, update the text and the cursor location
        if (hasChanged) {
            String newText = sb.toString();
            editText.setText(newText);
            editText.setSelection(currentSelection);
        }
    }

    private boolean isAllowed(char c) {
        // TODO: Add the filter logic here
        return Character.isLetter(c) || Character.isSpaceChar(c);
    }
    @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) {
        // Do Nothing
    }
});

The reason InputFilter is not a good solution in Android is since it depends on the keyboard implementation. The Keyboard input is being filtered before the input is passed to the EditText. But, because some keyboards have different implementations for the InputFilter.filter() invocation, this is problematic.

On the other hand TextWatcher does not care about the keyboard implementation, it allows us to create a simple solution and be sure it will work on all devices.

Soekarno answered 12/12, 2017 at 11:12 Comment(1)
The onTextChanged simply needs a public void in front of it.Crowl
G
1

If you subclass InputFilter you can create your own InputFilter that would filter out any non-alpha-numeric characters.

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.

Once you have created your own InputFilter, you can assign it to the EditText by calling setFilters(...).

http://developer.android.com/reference/android/text/InputFilter.html#filter(java.lang.CharSequence, int, int, android.text.Spanned, int, int)

Groundmass answered 28/7, 2010 at 2:28 Comment(0)
P
1

Ignoring the span stuff that other people have dealt with, to properly handle dictionary suggestions I found the following code works.

The source grows as the suggestion grows so we have to look at how many characters it's actually expecting us to replace before we return anything.

If we don't have any invalid characters, return null so that the default replacement occurs.

Otherwise we need to extract out the valid characters from the substring that's ACTUALLY going to be placed into the EditText.

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

        boolean includesInvalidCharacter = false;
        StringBuilder stringBuilder = new StringBuilder();

        int destLength = dend - dstart + 1;
        int adjustStart = source.length() - destLength;
        for(int i=start ; i<end ; i++) {
            char sourceChar = source.charAt(i);
            if(Character.isLetterOrDigit(sourceChar)) {
                if(i >= adjustStart)
                     stringBuilder.append(sourceChar);
            } else
                includesInvalidCharacter = true;
        }
        return includesInvalidCharacter ? stringBuilder : null;
    } 
}; 
Pumpernickel answered 7/8, 2014 at 20:44 Comment(0)
M
1

to prevent words in edittext. create a class that u could use anytime.

public class Wordfilter implements InputFilter
{
    @Override
    public CharSequence filter(CharSequence source, int start, int end,Spanned dest, int dstart, int dend) {
        // TODO Auto-generated method stub
        boolean append = false;
        String text = source.toString().substring(start, end);
        StringBuilder str = new StringBuilder(dest.toString());
        if(dstart == str.length())
        {
            append = true;
            str.append(text);
        }
        else
            str.replace(dstart, dend, text);
        if(str.toString().contains("aaaaaaaaaaaa/*the word here*/aaaaaaaa"))
        {
            if(append==true)
                return "";
            else
                return dest.subSequence(dstart, dend);
        }
        return null;
    }
}
Monumentalize answered 23/9, 2014 at 8:26 Comment(0)
N
0

It is possible to use setOnKeyListener. In this method, we can customize the input edittext !

Neighboring answered 12/12, 2011 at 17:58 Comment(0)
N
0

This is how I created filter for the Name field in Edit Text.(First letter is CAPS, and allow only single space after every word.

public void setNameFilter() {
    InputFilter filter = new InputFilter() {
        @RequiresApi(api = Build.VERSION_CODES.KITKAT)
        public CharSequence filter(CharSequence source, int start, int end,
                                   Spanned dest, int dstart, int dend) {
            for (int i = start; i < end; i++) {
                if (dend == 0) {
                    if (Character.isSpaceChar(source.charAt(i)) ||
                            !Character.isAlphabetic(source.charAt(i))) {
                        return Constants.Delimiter.BLANK;
                    } else {
                        return String.valueOf(source.charAt(i)).toUpperCase();
                    }
                } else if (Character.isSpaceChar(source.charAt(i)) &&
                        String.valueOf(dest).endsWith(Constants.Delimiter.ONE_SPACE)) {
                    return Constants.Delimiter.BLANK;
                } else if ((!Character.isSpaceChar(source.charAt(i)) &&
                        !Character.isAlphabetic(source.charAt(i)))) {
                    return Constants.Delimiter.BLANK;
                }
            }
            return null;
        }
    };
    editText.setFilters(new InputFilter[]{filter, new InputFilter.LengthFilter(Constants.Length.NAME_LENGTH)});
}
Nari answered 29/10, 2019 at 6:35 Comment(1)
Constants.Delimiter.BLANK is unkown.Groats
K
0

I have the same answer in Kotlin:

/**
 * Returns the filter of the editText'es CharSequence value when [filterType] is:
 * 1 -> letters; 2 -> letters and digits; 3 -> digits;
 * 4 -> digits and dots
 */
class InputFilterAlphanumeric(private val filterType: Int): InputFilter {
    override fun filter(source: CharSequence?, start: Int, end: Int, dest: Spanned?, dstart: Int, dend: Int): CharSequence {
        (source as? SpannableStringBuilder)?.let {sourceAsSpannableBuilder  ->
            for (i in (end - 1) downTo start) {
                val currentChar = source[i]
                when(filterType) {
                    1 -> {
                        if (!currentChar.isLetter() && !currentChar.isWhitespace()) {
                            sourceAsSpannableBuilder.delete(i, i + 1)
                        }
                    }
                    2 -> {
                        if (!currentChar.isLetterOrDigit() && !currentChar.isWhitespace()) {
                            sourceAsSpannableBuilder.delete(i, i + 1)
                        }
                    }
                    3 -> {
                        if (!currentChar.isDigit()) {
                            sourceAsSpannableBuilder.delete(i, i + 1)
                        }
                    }
                    4 -> {
                        if (!currentChar.isDigit() || !currentChar.toString().contains(".")) {
                            sourceAsSpannableBuilder.delete(i, i + 1)
                        }
                    }
                }
            }
            return source
        } ?: run {
            val filteredStringBuilder = StringBuilder()
            for (i in start until end) {
                val currentChar = source?.get(i)
                when(filterType) {
                    1 -> {
                        if (currentChar?.isLetter()!! || currentChar.isWhitespace()) {
                            filteredStringBuilder.append(currentChar)
                        }
                    }
                    2 -> {
                        if (currentChar?.isLetterOrDigit()!! || currentChar.isWhitespace()) {
                            filteredStringBuilder.append(currentChar)
                        }
                    }
                    3 -> {
                        if (currentChar?.isDigit()!!) {
                            filteredStringBuilder.append(currentChar)
                        }
                    }
                    4 -> {
                        if (currentChar?.isDigit()!! || currentChar.toString().contains(".")) {
                            filteredStringBuilder.append(currentChar)
                        }
                    }
                }
            }
            return filteredStringBuilder
        }
    }
}

and get the class with an Extension function:

fun EditText.filterByDataType(filterType: Int) {
    this.filters = arrayOf<InputFilter>(InputFilterAlphanumeric(filterType))
}
Killer answered 21/2, 2020 at 23:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.