Wie verwende ich InputFilter, um Zeichen in einem EditText in Android einzuschränken?

171

Ich möchte die Zeichen nur auf 0-9, az, AZ und die Leertaste beschränken. Festlegen des Eingabetyps Ich kann mich auf Ziffern beschränken, aber ich kann nicht herausfinden, wie Inputfilter die Dokumente durchsucht.

Tim Wayne
quelle

Antworten:

186

Ich habe das in einem anderen Forum gefunden. Funktioniert wie ein 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 });
Mondscheinkäse
quelle
58
Tatsächlich funktioniert es in neueren Androiden (wie 4.0+) nicht so gut. Sie führen Wörterbuchvorschläge über der Tastatur ein. Wenn Sie ein allgemeines Wort eingeben (sagen wir "das"), gefolgt von einem unzulässigen Zeichen für diesen Filter (sagen wir "-"), wird das gesamte Wort gelöscht und nachdem Sie ein anderes Zeichen eingegeben haben (auch erlaubte Zeichen wie "bla"). Der Filter gibt "" zurück und es wird kein Zeichen im Feld angezeigt. Dies liegt daran, dass die Methode einen SpannableStringBuilder im Quellparameter mit "the-blah" und Start- / Endparametern erhält, die sich über die gesamte Eingabezeichenfolge erstrecken ... Eine bessere Lösung finden Sie in meiner Antwort.
Łukasz Sromek
4
In diesem Beispiel, in dem "" zurückgegeben wird, sollte der angezeigte Text zurückgegeben werden. Das heißt, Sie sollten die unzulässigen Zeichen entfernen und die Zeichenfolge zurückgeben, die Sie anzeigen möchten. developer.android.com/reference/android/widget/… , android.view.KeyEvent)
Andrew Mackenzie
+1 Wirklich eine gute Antwort. Character.isLetterOrDigit () Alle diese Methoden sind sehr nützlich.
Atul Bhardwaj
@AndrewMackenzie Wenn das Eingabezeichen, zum Beispiel ein Komma ',', das nicht legal ist, und ich es entfernen möchte, wie man den obigen Code repariert.
Twlkyao
! Character.isLetterOrDigit (source.charAt (i)) &&! Character.isSpaceChar (source.charAt (i)), um Leerzeichen zuzulassen
Leon
139

InputFilters sind in Android-Versionen, in denen Wörterbuchvorschläge angezeigt werden, etwas kompliziert. Manchmal erhalten Sie eine SpannableStringBuilder, manchmal eine Ebene Stringim sourceParameter.

Folgendes InputFiltersollte funktionieren. Fühlen Sie sich frei, diesen Code zu verbessern!

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
quelle
2
Gibt es einen Grund, warum Sie die Quelle nicht nachfolgen möchten? Sehen Sie etwas falsch daran, dies nur zu tun (um nur alphanumerische Zeichen plus einige Sonderzeichen zuzulassen): String replacement = source.subSequence(start, end).toString(); return replacement.replaceAll("[^A-Za-z0-9_\\-@]", "");
Splash
3
Dies berücksichtigt kein Problem, bei dem sich wiederholender Wörterbuchvorschlagstext angezeigt wird. @serwus identifizierte das in ihrer Antwort. Grundsätzlich sollten Sie null zurückgeben, wenn in beiden Fällen keine Änderungen vorgenommen werden.
hooby3dfx
3
Dieser Code funktioniert perfekt, wie Lukasas sagte (in der obigen Antwort), aber ich habe ein Problem mit den Nicht-Wörterbuch-Wörtern. Wenn ich Chiru tippe, zeigt es 3 Mal wie Chiruchiruchiru. Wie man es löst? und es braucht auch Leerzeichen, aber wie kann man neben den nächsten Leerzeichen einschränken?
Chiru
3
Aus irgendeinem Grund gibt mir die source instanceof SpannableStringBuilderEingabe von AB AAB, wie wenn ich die vorherige Antwort versuche. Glücklicherweise konnte ich es mithilfe der folgenden @ florian-Lösung umgehen.
Guillaume
1
@ hooby3dfx ist absolut richtig. Während es gelingt, die Zeichenfolge irgendwann richtig zu machen, wird es durch die Verwendung der Wörterbuch- / Wortvervollständigung durcheinander gebracht.
Aravind
107

viel einfacher:

<EditText
    android:inputType="text"
    android:digits="0,1,2,3,4,5,6,7,8,9,*,qwertzuiopasdfghjklyxcvbnm" />
Florian Fröhlich
quelle
9
Obwohl dies eine perfekte Antwort zu sein scheint, gibt es laut Dokumentation ein Problem: "Wie bei allen Implementierungen von KeyListener befasst sich diese Klasse nur mit Hardwaretastaturen. Software-Eingabemethoden sind nicht verpflichtet, die Methoden in dieser Klasse auszulösen." Ich denke, ein InputFilter ist wahrscheinlich ein besserer, wenn auch komplizierterer Weg.
Craig B
19
Awesome Solution Nur hinzufügen möchten Sie müssen nicht ","dazwischen geben. Sie können so etwas verwenden"0123456789qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM"
DeltaCap019
26
Keine mehrsprachige Lösung
AAverin
Wenn Sie vorhaben, Übersetzungen in Ihrer App zu verwenden. VERWENDEN SIE DIESE LÖSUNG NICHT!
Grantespo
Scheint so, als würde dies möglicherweise nicht funktionieren imeOptions="actionNext"usw.
Pete Doyle
68

Keine der geposteten Antworten hat bei mir funktioniert. Ich kam mit meiner eigenen Lösung:

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
quelle
3
Dies ist die einzige Antwort, die tatsächlich den richtigen Ansatz hat, um zu verhindern, dass sich Text aus Wörterbuchvorschlägen wiederholt! Upvote!
hooby3dfx
4
Das einzige, was EditTextbereits eigene Filter haben kann, zB Längenfilter. Anstatt nur die Filter zu überschreiben, möchten Sie Ihre Filter höchstwahrscheinlich zu bereits vorhandenen hinzufügen.
Aleks N.
Ist das noch aktuell? Für mich funktioniert Android 6.0.1 auf der Bildschirmtastatur
XxGoliathusxX
2
Ein kleines Problem bei dieser Antwort (oder bei der Funktionsweise des Eingabemechanismus von Android) ist, dass die Rücktaste dem Benutzer manchmal als nicht funktionierend erscheint, da sie einen Abstand zu zuvor eingegebenen ungültigen Zeichen aufweist, die noch im Quellpuffer gespeichert sind.
jk7
1
Gleiches Problem wie bei der Lösung von Łukasz Sromek: Ein ungültiges Zeichen nach einem Leerzeichen wird durch ein Leerzeichen ersetzt.
Ingo Schalk-Schupp
25

Verwenden Sie dies seine Arbeit 100% Ihre Bedürfnisse und sehr einfach.

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

In strings.xml

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

Um Sonderzeichen im Eingabetyp zu vermeiden

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

Sie können den Filter wie unten beschrieben auf Ihren Bearbeitungstext einstellen

edtText.setFilters(new InputFilter[] { filter });
CommonGuy
quelle
@sathya Sie wc, glücklich, Ihnen zu helfen :)
1
Blockiert keines dieser Zeichen.
Anarchofaschist
7

Zusätzlich zur akzeptierten Antwort ist es auch möglich, zB: android:inputType="textCapCharacters"als Attribut von <EditText>zu verwenden, um nur Großbuchstaben (und Zahlen) zu akzeptieren.

mblenton
quelle
5
android: inputType = "textCapCharacters" beschränkt sich nicht auf die Verwendung anderer Zeichen wie '., - "usw.
Tvd
Es ist auch nur ein Hinweis auf die Eingabemethode. Es schränkt nicht ein, welche Zeichen eingegeben werden dürfen.
dcow
5

Aus irgendeinem Grund ist der Konstruktor der android.text.LoginFilter-Klasse paketbasiert, sodass Sie ihn nicht direkt erweitern können (obwohl er mit diesem Code identisch wäre). Sie können LoginFilter.UsernameFilterGeneric jedoch erweitern! Dann haben Sie einfach Folgendes:

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

Dies ist nicht wirklich dokumentiert, aber es ist Teil der Kernbibliothek und die Quelle ist unkompliziert . Ich benutze es jetzt schon eine Weile, bisher keine Probleme, obwohl ich zugeben muss, dass ich nichts Komplexes mit spannables versucht habe.

Groxx
quelle
5

Es ist richtig, der beste Weg, dies im XML-Layout selbst zu beheben, indem Sie Folgendes verwenden:

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

Wie Florian Fröhlich zu Recht betont, eignet es sich auch gut für Textansichten.

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

Nur ein Wort der Vorsicht, die in der genannten Zeichen android:digitswerden nur angezeigt. Achten Sie also darauf, dass Sie keine Zeichen verpassen :)

Kailas
quelle
Sie müssen inputType = "textFilter" definieren, dann funktioniert nur es ordnungsgemäß.
Shreyash Mahajan
@ShreyashMahajan, hängt es von der Geräte- / Tastatur-App ab? In meinem Fall hat inputTypedies keinen Einfluss auf die Filterung.
CoolMind
4

Diese einfache Lösung funktionierte für mich, als ich verhindern musste, dass der Benutzer leere Zeichenfolgen in einen EditText eingibt. Sie können natürlich weitere Zeichen hinzufügen:

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

}
Schneller McSwifterton
quelle
Wie nennt man diese Lösung?
Zeeks
1

Wenn Sie InputFilter unterordnen, können Sie Ihren eigenen InputFilter erstellen, der alle nicht alphanumerischen Zeichen herausfiltert.

Die InputFilter-Schnittstelle verfügt über eine Methode: filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) und bietet Ihnen alle Informationen, die Sie benötigen, um zu wissen, welche Zeichen in den EditText eingegeben wurden, dem sie zugewiesen sind.

Nachdem Sie Ihren eigenen InputFilter erstellt haben, können Sie ihn dem EditText zuweisen, indem Sie setFilters (...) aufrufen.

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

nicholas.hauschild
quelle
1

Ich ignorierte das Span-Zeug, mit dem sich andere Leute befasst haben, um Wörterbuchvorschläge richtig zu handhaben. Ich fand, dass der folgende Code funktioniert.

Die Quelle wächst mit dem Vorschlag, daher müssen wir uns ansehen, wie viele Zeichen wir tatsächlich ersetzen sollen, bevor wir etwas zurückgeben.

Wenn wir keine ungültigen Zeichen haben, geben Sie null zurück, damit die Standardersetzung erfolgt.

Andernfalls müssen wir die gültigen Zeichen aus dem Teilstring extrahieren, der tatsächlich in den EditText eingefügt wird.

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
quelle
1

um Wörter in edittext zu verhindern. Erstelle eine Klasse, die du jederzeit verwenden kannst.

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
quelle
1

Zuerst hinzufügen in 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
}

Seltsam, aber es funktioniert komisch auf der Softtastatur des Emulators.

Warnung! Der folgende Code filtert alle Buchstaben und anderen Symbole mit Ausnahme der Ziffern für Software-Tastaturen. Auf Smartphones wird nur die digitale Tastatur angezeigt.

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

Ich habe auch in der Regel eingestellt maxLength, filters, inputType.

CoolMind
quelle
1

Dies ist ein alter Thread, aber die beabsichtigten Lösungen haben alle Probleme (abhängig von Gerät / Android-Version / Tastatur).

VERSCHIEDENER ANSATZ

Also habe ich mich irgendwann für einen anderen Ansatz entschieden, anstatt die InputFilterproblematische Implementierung zu verwenden, die ich verwende, TextWatcherund die TextChangedListenerder EditText.

VOLLSTÄNDIGER CODE (BEISPIEL)

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

Der Grund, warum InputFilterAndroid keine gute Lösung ist, liegt darin, dass es von der Tastaturimplementierung abhängt. Die Tastatureingabe wird gefiltert, bevor die Eingabe an die übergeben wird EditText. InputFilter.filter()Dies ist jedoch problematisch , da einige Tastaturen unterschiedliche Implementierungen für den Aufruf haben.

Auf der anderen Seite TextWatcherkümmert sich die Tastaturimplementierung nicht darum, sondern ermöglicht es uns, eine einfache Lösung zu erstellen und sicherzustellen, dass sie auf allen Geräten funktioniert.

Eyal Biran
quelle
Das onTextChangedbraucht einfach ein public voiddavor.
Andy
Danke für den Kommentar. Fest.
Eyal Biran
1

Ich habe so etwas getan, um es einfach zu halten:

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 ]"), "")
    }
})

Auf diese Weise ersetzen wir alle unerwünschten Zeichen im neuen Teil der Quellzeichenfolge durch eine leere Zeichenfolge.

Die edit_textVariable ist dieEditText Objekt, auf das wir uns beziehen.

Der Code ist in geschrieben kotlin.

Lazar
quelle
1
Vielen Dank! Diese Lösung funktioniert gut beim Eingeben und Einfügen eines Textes.
CoolMind
0

Es ist möglich zu verwenden setOnKeyListener. In dieser Methode können wir die Eingabe anpassen edittext!

Võ Hoài Lên
quelle
0

Auf diese Weise habe ich einen Filter für das Feld Name in Text bearbeiten erstellt. (Der erste Buchstabe ist GROSSBUCHSTABEN. Nach jedem Wort darf nur ein Leerzeichen stehen.

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
quelle
Constants.Delimiter.BLANK ist unbekannt.
CoolMind
0

Ich habe die gleiche Antwort 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
        }
    }
}

und erhalten Sie die Klasse mit einer Erweiterungsfunktion:

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