Comment utiliser InputFilter pour limiter les caractères dans un EditText sous Android?

171

Je veux limiter les caractères à 0-9, az, AZ et barre d'espace uniquement. Définition du type d'entrée Je peux me limiter aux chiffres, mais je ne peux pas comprendre les façons dont Inputfilter regarde à travers les documents.

Tim Wayne
la source

Réponses:

186

J'ai trouvé ça sur un autre forum. Fonctionne comme un champion.

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 });
fromage au clair de lune
la source
58
en fait, cela ne fonctionne pas aussi bien dans les nouveaux androïdes (comme 4.0+). Ils introduisent des suggestions de dictionnaires au-dessus du clavier. Lorsque vous tapez un mot courant (disons "le") suivi d'un caractère illégal pour ce filtre (disons "-"), le mot entier est supprimé et après avoir tapé un autre caractère (même les caractères autorisés, comme "bla") le filtre renvoie "" et aucun caractère n'apparaît dans le champ. C'est parce que la méthode obtient un SpannableStringBuilder dans le paramètre source avec "the-blah" dedans et des paramètres de début / fin couvrant toute la chaîne d'entrée ... Voir ma réponse pour une meilleure solution.
Łukasz Sromek
4
Dans cet exemple, où il renvoie "", je pense qu'il devrait renvoyer le texte qui devrait être affiché. c'est-à-dire que vous devez supprimer les caractères illégaux et renvoyer la chaîne que vous VOULEZ afficher. developer.android.com/reference/android/widget/… , android.view.KeyEvent)
Andrew Mackenzie
+1 Vraiment une excellente réponse. Character.isLetterOrDigit () toutes ces méthodes sont très utiles.
Atul Bhardwaj
@AndrewMackenzie Si le caractère d'entrée, par exemple, une virgule ',', ce qui n'est pas légal, et je veux le supprimer, comment corriger le code ci-dessus.
twlkyao
! Character.isLetterOrDigit (source.charAt (i)) &&! Character.isSpaceChar (source.charAt (i)) pour permettre des espaces
Léon
139

InputFilterLes versions d'Android qui affichent des suggestions de dictionnaires sont un peu compliquées. Vous obtenez parfois un SpannableStringBuilder, parfois un simple Stringdans le sourceparamètre.

Ce qui suit InputFilterdevrait fonctionner. N'hésitez pas à améliorer ce 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();
        }
    }
}
Łukasz Sromek
la source
2
y a-t-il une raison pour laquelle vous ne voudriez pas mettre en séquence la source? Voyez-vous quelque chose de mal à faire cela (afin de n'autoriser que les alphanumériques plus quelques caractères spéciaux): String replacement = source.subSequence(start, end).toString(); return replacement.replaceAll("[^A-Za-z0-9_\\-@]", "");
Splash
3
Cela ne prend pas en compte un problème où la répétition du texte de suggestion de dictionnaire apparaît. @serwus a identifié cela dans sa réponse. En gros, vous devez retourner null si aucune modification n'est apportée dans les deux cas.
hooby3dfx
3
Ce code fonctionne parfaitement comme lukasz l'a dit (dans la réponse ci-dessus) mais je suis confronté à un problème avec les mots non-dictionnaires. quand je tape chiru il montre 3 fois comme chiruchiruchiru. comment le résoudre? et cela prend aussi des espaces blancs mais comment restreindre à côté des prochains espaces blancs?
chiru
3
Pour une raison quelconque, quand source instanceof SpannableStringBuilderentrer AB me donne AAB comme lorsque j'essaye la réponse précédente. Heureusement, j'ai pu contourner ce problème en utilisant la solution @florian ci-dessous.
Guillaume le
1
@ hooby3dfx a tout à fait raison. Bien qu'il parvienne finalement à obtenir la bonne chaîne, cela gâche lorsque vous utilisez la complétion dictionnaire / mot.
Aravind
107

beaucoup plus facile:

<EditText
    android:inputType="text"
    android:digits="0,1,2,3,4,5,6,7,8,9,*,qwertzuiopasdfghjklyxcvbnm" />
Florian Fröhlich
la source
9
Bien que cela semble être une réponse parfaite, il y a un problème selon la documentation: "Comme pour toutes les implémentations de KeyListener, cette classe ne concerne que les claviers matériels. Les méthodes d'entrée logicielles n'ont aucune obligation de déclencher les méthodes de cette classe." Je pense qu'un InputFilter est probablement une meilleure solution, bien que plus compliquée.
Craig B
19
Solution géniale Vous voulez juste ajouter Vous n'avez pas besoin de céder ","entre les deux. Vous pouvez utiliser quelque chose comme ça"0123456789qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM"
DeltaCap019
26
Pas une solution multilingue
AAverin
Si vous prévoyez d'utiliser des traductions dans votre application. N'UTILISEZ PAS CETTE SOLUTION!
grantespo
On dirait que cela peut ne pas fonctionner avec imeOptions="actionNext", etc.
Pete Doyle
68

Aucune des réponses publiées n'a fonctionné pour moi. Je suis venu avec ma propre 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 });
Kamil Seweryn
la source
3
C'est la seule réponse qui a réellement la bonne approche pour éviter de répéter le texte des suggestions de dictionnaire! Upvote!
hooby3dfx
4
La seule chose, EditTextpeut déjà avoir ses propres filtres, par exemple un filtre de longueur. Ainsi, au lieu de simplement remplacer les filtres, vous souhaiterez probablement ajouter vos filtres à ceux déjà existants.
Aleks N.
Est-ce toujours à jour? Pour moi Android 6.0.1 cela fonctionne sur le clavier à l'écran
XxGoliathusxX
2
Un problème mineur avec cette réponse (ou avec le fonctionnement du mécanisme de saisie d'Android) est que le retour arrière semble parfois à l'utilisateur ne pas fonctionner car il effectue un retour arrière sur des caractères invalides précédemment saisis qui sont toujours conservés dans la mémoire tampon source.
jk7
1
Même problème qu'avec la solution de Łukasz Sromek: un caractère invalide après un espace est remplacé par un espace.
Ingo Schalk-Schupp
25

Utilisez ce travail à 100% selon vos besoins et très simple.

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

Dans strings.xml

<string name="myAlphaNumeric">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</string>
Mohamed Ibrahim
la source
15

Pour éviter les caractères spéciaux dans le type d'entrée

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;
    }
};

Vous pouvez définir un filtre sur votre texte d'édition comme ci-dessous

edtText.setFilters(new InputFilter[] { filter });
CommonGuy
la source
@sathya You wc, Happy to help you :)
1
Ne bloque aucun de ces personnages. ㋡ ㋛ ☺ ☹ ☻ 〠シッツヅÜ 〲 〴 ϡ ٿ ت ⍡ ⍢ ⍣
Anarchofascist
7

En plus de la réponse acceptée, il est également possible d'utiliser par exemple: android:inputType="textCapCharacters"comme attribut de <EditText>pour n'accepter que les majuscules (et les nombres).

mblenton
la source
5
android: inputType = "textCapCharacters" ne se limite pas à l'utilisation d'autres caractères tels que '., - "etc.
Tvd
Ce n'est également qu'un indice sur la méthode d'entrée. Il ne restreint pas les caractères autorisés à être saisis.
dcow
5

Pour une raison quelconque, le constructeur de la classe android.text.LoginFilter a une portée globale, vous ne pouvez donc pas l'étendre directement (même s'il serait identique à ce code). Mais vous pouvez étendre LoginFilter.UsernameFilterGeneric! Ensuite, vous avez juste ceci:

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;
    }
}

Ce n'est pas vraiment documenté, mais cela fait partie de la bibliothèque principale, et la source est simple . Je l'utilise depuis un moment maintenant, jusqu'à présent aucun problème, même si j'avoue que je n'ai pas essayé de faire quelque chose de complexe impliquant des spannables.

Groxx
la source
5

C'est vrai, la meilleure façon de procéder pour le corriger dans la mise en page XML elle-même en utilisant:

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

comme l'a justement souligné Florian Fröhlich, cela fonctionne bien pour les vues de texte même.

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

Juste un mot d'avertissement, les caractères mentionnés dans le android:digitsseront uniquement affichés, alors faites attention à ne manquer aucun jeu de caractères :)

Kailas
la source
vous devez définir inputType = "textFilter" alors seulement cela fonctionnera correctement.
Shreyash Mahajan
@ShreyashMahajan, cela dépend-il de l'application appareil / clavier? Dans mon cas, inputTypecela n'affecte pas le filtrage.
CoolMind
4

Cette solution simple a fonctionné pour moi lorsque je devais empêcher l'utilisateur d'entrer des chaînes vides dans un EditText. Vous pouvez bien sûr ajouter plus de caractères:

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});

}
Swifty McSwifterton
la source
comment appeler cette solution?
zeeks
1

Si vous sous-classez InputFilter, vous pouvez créer votre propre InputFilter qui filtrerait tous les caractères non alphanumériques.

L'interface InputFilter a une méthode, filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend)et elle vous fournit toutes les informations dont vous avez besoin pour savoir quels caractères ont été entrés dans le EditText auquel il est affecté.

Une fois que vous avez créé votre propre InputFilter, vous pouvez l'attribuer à EditText en appelant setFilters (...).

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

nicholas.hauschild
la source
1

En ignorant les problèmes de span que d'autres personnes ont traités, pour gérer correctement les suggestions de dictionnaire, j'ai trouvé que le code suivant fonctionne.

La source grandit au fur et à mesure que la suggestion grandit, nous devons donc examiner le nombre de caractères que nous attendons réellement de nous remplacer avant de retourner quoi que ce soit.

Si nous n'avons pas de caractères non valides, retournez null pour que le remplacement par défaut se produise.

Sinon, nous devons extraire les caractères valides de la sous-chaîne qui va VRAIMENT être placée dans 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;
    } 
}; 
Christian Whitehouse
la source
1

pour empêcher les mots dans edittext. créez une classe que vous pourriez utiliser à tout moment.

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;
    }
}
Sahar Millis
la source
1

Ajoutez d'abord dans strings.xml:

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

XML :

android:digits="@string/vin_code_mask"

Code en 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
}

Étrange, mais cela fonctionne bizarrement sur le clavier virtuel de l'émulateur.

Avertissement! Le code suivant filtrera toutes les lettres et autres symboles à l'exception des chiffres des claviers logiciels. Seul le clavier numérique apparaîtra sur les smartphones.

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

Je habituellement également mis maxLength, filters, inputType.

CoolMind
la source
1

Il s'agit d'un ancien fil de discussion, mais les solutions proposées ont toutes des problèmes (selon l'appareil / la version Android / le clavier).

UNE APPROCHE DIFFÉRENTE

Donc, finalement, je suis allé avec une approche différente, au lieu d'utiliser l' InputFilterimplémentation problématique, j'utilise TextWatcheret TextChangedListenerle EditText.

CODE COMPLET (EXEMPLE)

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
    }
});

La raison InputFiltern'est pas une bonne solution sous Android car cela dépend de l'implémentation du clavier. L'entrée Clavier est filtrée avant que l'entrée ne soit transmise au EditText. Mais, comme certains claviers ont des implémentations différentes pour l' InputFilter.filter()invocation, cela est problématique.

D'autre part, TextWatcherne se soucie pas de l'implémentation du clavier, cela nous permet de créer une solution simple et d'être sûr qu'elle fonctionnera sur tous les appareils.

Eyal Biran
la source
Le a onTextChangedsimplement besoin d'un public voiddevant lui.
Andy
Merci pour le commentaire. Fixé.
Eyal Biran
1

J'ai fait quelque chose comme ça pour rester 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 ]"), "")
    }
})

De cette façon, nous remplaçons tous les caractères indésirables dans la nouvelle partie de la chaîne source par une chaîne vide.

La edit_textvariable est l' EditTextobjet auquel nous nous référons.

Le code est écrit kotlin.

Lazar
la source
1
Merci! Cette solution fonctionne bien lors de la saisie et du collage d'un texte.
CoolMind
0

Il est possible d'utiliser setOnKeyListener. Dans cette méthode, nous pouvons personnaliser l'entrée edittext!

Võ Hoài Lên
la source
0

C'est ainsi que j'ai créé un filtre pour le champ Nom dans Modifier le texte (la première lettre est MAJUSCULES et n'autorise qu'un seul espace après chaque mot.

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)});
}
u_pendra
la source
Constants.Delimiter.BLANK est inconnu.
CoolMind
0

J'ai la même réponse à 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
        }
    }
}

et récupérez la classe avec une fonction d'extension:

fun EditText.filterByDataType(filterType: Int) {
    this.filters = arrayOf<InputFilter>(InputFilterAlphanumeric(filterType))
}
Irving Kennedy
la source