Jak używać InputFilter, aby ograniczyć liczbę znaków w EditText w systemie Android?

171

Chcę ograniczyć znaki tylko do 0-9, az, AZ i spacji. Ustawiając inputtype mogę ograniczyć się do cyfr, ale nie mogę znaleźć sposobów, w jakie Inputfilter przegląda dokumenty.

Tim Wayne
źródło

Odpowiedzi:

186

Znalazłem to na innym forum. Działa jak mistrz.

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 });
ser księżycowy
źródło
58
w rzeczywistości nie działa tak dobrze w nowszych Androidach (np. 4.0+). Nad klawiaturą wprowadzają sugestie ze słownika. Kiedy wpisujesz typowe słowo (powiedzmy „the”), po którym następuje niedozwolony znak dla tego filtru (powiedzmy „-”), całe słowo jest usuwane i po wpisaniu kolejnych znaków (nawet dozwolonych, np. „Bla”) filtr zwraca „” i żaden znak nie pojawia się w polu. Dzieje się tak, ponieważ metoda pobiera SpannableStringBuilder w parametrze źródłowym z "the-blah" i parametrami początkowymi / końcowymi obejmującymi cały ciąg wejściowy ... Zobacz moją odpowiedź dla lepszego rozwiązania.
Łukasz Sromek
4
W tym przykładzie, gdy zwraca „”, myślę, że powinien zwrócić tekst, który powinien zostać wyświetlony. tzn. powinieneś usunąć niedozwolone znaki i zwrócić napis, który CHCESZ wyświetlić. developer.android.com/reference/android/widget/… , android.view.KeyEvent)
Andrew Mackenzie,
+1 Naprawdę świetna odpowiedź. Character.isLetterOrDigit () wszystkie te metody są bardzo przydatne.
Atul Bhardwaj
@AndrewMackenzie Jeśli znak wejściowy, na przykład przecinek „,”, który nie jest legalny, a chcę go usunąć, jak naprawić powyższy kod.
twlkyao
! Character.isLetterOrDigit (source.charAt (i)) &&! Character.isSpaceChar (source.charAt (i)), aby uwzględnić spacje
Leon
139

InputFilterSą trochę skomplikowane w wersjach Androida, które wyświetlają sugestie ze słownika. Czasami w parametrze pojawia się a SpannableStringBuilder, czasami zwykły .Stringsource

Poniższe InputFilterpowinny działać. Zapraszam do ulepszania tego kodu!

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
źródło
2
Czy jest powód, dla którego nie chciałbyś podporządkować źródła? Czy widzisz coś złego w robieniu tego (aby zezwolić tylko na znaki alfanumeryczne i kilka znaków specjalnych): String replacement = source.subSequence(start, end).toString(); return replacement.replaceAll("[^A-Za-z0-9_\\-@]", "");
Splash
3
Nie uwzględnia to problemu, w którym pojawia się powtarzający się tekst podpowiedzi ze słownika. @serwus zidentyfikował to w swojej odpowiedzi. Zasadniczo powinieneś zwrócić wartość null, jeśli w obu przypadkach nie zostaną wprowadzone żadne modyfikacje.
hooby3dfx
3
Ten kod działa idealnie, jak powiedział Łukasz (w powyższej odpowiedzi), ale mam problem ze słowami spoza słownika. kiedy piszę chiru, pokazuje 3 razy jak chiruchiruchiru. jak to rozwiązać? i zajmuje też spacje, ale jak ograniczyć obok następnych białych spacji?
chiru
3
Z jakiegoś powodu, kiedy source instanceof SpannableStringBuilderwpisanie AB daje mi AAB, tak jak przy próbowaniu poprzedniej odpowiedzi. Na szczęście udało mi się to obejść, korzystając z rozwiązania @florian poniżej.
Guillaume,
1
@ hooby3dfx ma całkowitą rację. Chociaż w końcu udaje mu się uzyskać prawidłowy ciąg, powoduje to bałagan, gdy używasz słownika / uzupełniania słów.
Aravind
107

dużo łatwiej:

<EditText
    android:inputType="text"
    android:digits="0,1,2,3,4,5,6,7,8,9,*,qwertzuiopasdfghjklyxcvbnm" />
Florian Fröhlich
źródło
9
Chociaż wydaje się to idealną odpowiedzią, zgodnie z dokumentacją istnieje problem: „Tak jak w przypadku wszystkich implementacji KeyListener, ta klasa dotyczy tylko klawiatur sprzętowych. Metody wejściowe oprogramowania nie mają obowiązku wyzwalania metod w tej klasie”. Myślę, że InputFilter to prawdopodobnie lepszy, choć bardziej skomplikowany sposób.
Craig B
19
Niesamowite rozwiązanie, które po prostu chcesz dodać Nie musisz się poddawać ",". Możesz użyć czegoś takiego"0123456789qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM"
DeltaCap019
26
To nie jest wielojęzyczne rozwiązanie
AAverin
Jeśli planujesz używać tłumaczeń w swojej aplikacji. NIE UŻYWAJ TEGO ROZWIĄZANIA!
grantespo
Wygląda na to, że może to nie działać imeOptions="actionNext"itp.
Pete Doyle
68

Żadna z opublikowanych odpowiedzi nie zadziałała dla mnie. Przyszedłem z własnym rozwiązaniem:

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
źródło
3
To jedyna odpowiedź, która faktycznie ma właściwe podejście, aby zapobiec powtarzaniu się tekstu z sugestii ze słownika! Głosuj za!
hooby3dfx
4
Jedyne, co EditTextmoże mieć już własne filtry, np. Filtr długości. Dlatego zamiast po prostu zastępować filtry, najprawdopodobniej chcesz dodać filtry do już istniejących.
Aleks N.
Czy to wciąż aktualne? U mnie Android 6.0.1 działa na klawiaturze ekranowej
XxGoliathusxX
2
Drobnym problemem związanym z tą odpowiedzią (lub ze sposobem działania mechanizmu wprowadzania danych w systemie Android) jest to, że użytkownik czasami wydaje się nie działać, ponieważ cofają się w stosunku do wcześniej wprowadzonych nieprawidłowych znaków, które nadal są przechowywane w buforze źródłowym.
jk7
1
Ten sam problem co w rozwiązaniu Łukasza Sromka: nieprawidłowy znak występujący po spacji jest zastępowany spacją.
Ingo Schalk-Schupp
25

Wykorzystaj to w 100% swojej pracy i bardzo proste.

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

W strings.xml

<string name="myAlphaNumeric">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</string>
Mohamed Ibrahim
źródło
15

Aby uniknąć znaków specjalnych w typie wprowadzania

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

Możesz ustawić filtr do tekstu edycji, jak poniżej

edtText.setFilters(new InputFilter[] { filter });
CommonGuy
źródło
@sathya You wc, Chętnie Ci pomogę :)
1
Nie blokuje żadnego z tych znaków. ㋡ ㋛ ☺ ☹ ☻ 〠シッツヅÜ 〲 〴 ϡ ٿ ت ⍡ ⍢ ⍣
Anarchofascist
7

Oprócz zaakceptowanej odpowiedzi możliwe jest również użycie np .: android:inputType="textCapCharacters"jako atrybutu <EditText>w celu akceptowania wyłącznie wielkich liter (i cyfr).

mblenton
źródło
5
android: inputType = "textCapCharacters" nie ogranicza używania innych znaków, takich jak „., -” itp.
Tvd
To także tylko wskazówka co do metody wprowadzania. Nie ogranicza to, jakie znaki można wprowadzać.
dcow
5

Z jakiegoś powodu konstruktor klasy android.text.LoginFilter jest objęty zakresem pakietu, więc nie można go bezpośrednio rozszerzyć (nawet jeśli byłby identyczny z tym kodem). Ale możesz rozszerzyć LoginFilter.UsernameFilterGeneric! Wtedy po prostu masz to:

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

Nie jest to tak naprawdę udokumentowane, ale jest częścią podstawowej biblioteki, a źródło jest proste . Używam go już od jakiegoś czasu, do tej pory żadnych problemów, chociaż przyznaję, że nie próbowałem robić nic skomplikowanego z udziałem spannables.

Groxx
źródło
5

To prawda, najlepszym sposobem na naprawienie tego w samym układzie XML za pomocą:

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

jak słusznie zauważył Florian Fröhlich, działa dobrze nawet w przypadku widoków tekstowych.

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

Tylko słowo ostrzeżenia, znaki wymienione w android:digitsbędą wyświetlane tylko, więc uważaj, aby nie przegapić żadnego zestawu znaków :)

Kailas
źródło
musisz zdefiniować inputType = "textFilter", wtedy tylko będzie działać poprawnie.
Shreyash Mahajan
@ShreyashMahajan, czy to zależy od aplikacji na urządzeniu / klawiaturze? W moim przypadku inputTypenie wpływa na filtrowanie.
CoolMind
4

To proste rozwiązanie zadziałało, gdy musiałem uniemożliwić użytkownikowi wprowadzanie pustych ciągów do tekstu edycji. Możesz oczywiście dodać więcej znaków:

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
źródło
jak nazwać to rozwiązanie?
zeeks
1

Jeśli utworzysz podklasę InputFilter, możesz utworzyć własny InputFilter, który odfiltrowałby wszelkie znaki inne niż alfanumeryczne.

Interfejs InputFilter ma jedną metodę filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend)i zapewnia wszystkie potrzebne informacje o tym, które znaki zostały wprowadzone do EditText, do którego jest przypisany.

Po utworzeniu własnego filtra InputFilter możesz przypisać go do EditText, wywołując setFilters (...).

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

nicholas.hauschild
źródło
1

Ignorując rzeczy z zakresu rozpiętości, z którymi mieli do czynienia inni ludzie, aby poprawnie obsłużyć sugestie ze słownika, stwierdziłem, że następujący kod działa.

Źródło rośnie wraz ze wzrostem sugestii, więc musimy przyjrzeć się, ile znaków faktycznie oczekuje, że zastąpimy, zanim cokolwiek zwrócimy.

Jeśli nie mamy żadnych nieprawidłowych znaków, zwróć wartość null, aby nastąpiła domyślna zamiana.

W przeciwnym razie musimy wyodrębnić prawidłowe znaki z podciągu, który AKTUALNIE zostanie umieszczony w 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
źródło
1

aby zapobiec słowom w edittext. stwórz klasę, której będziesz mógł użyć w dowolnym momencie.

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
źródło
1

Najpierw dodaj do strings.xml:

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

XML :

android:digits="@string/vin_code_mask"

Kod w Kotlinie:

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
}

Dziwne, ale działa dziwnie na miękkiej klawiaturze emulatora.

Ostrzeżenie! Poniższy kod odfiltruje wszystkie litery i inne symbole z wyjątkiem cyfr w klawiaturach programowych. Na smartfonach pojawi się tylko klawiatura cyfrowa.

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

Ja też zwykle ustawione maxLength, filters, inputType.

CoolMind
źródło
1

To stary wątek, ale wszystkie celowe rozwiązania mają problemy (w zależności od urządzenia / wersji Androida / klawiatury).

ODMIENNE PODEJŚCIE

Więc w końcu pojechałem z innego podejścia, zamiast korzystania z InputFilterproblematyczną wdrażania, używam TextWatcheri TextChangedListenerz EditText.

PEŁNY KOD (PRZYKŁAD)

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

Powodem InputFilternie jest dobre rozwiązanie w systemie Android, ponieważ zależy to od implementacji klawiatury. Dane wejściowe z klawiatury są filtrowane przed przekazaniem ich do EditText. Ale ponieważ niektóre klawiatury mają różne implementacje InputFilter.filter()wywołania, jest to problematyczne.

Z drugiej strony TextWatchernie dba o implementację klawiatury, pozwala nam stworzyć proste rozwiązanie i mieć pewność, że zadziała na wszystkich urządzeniach.

Eyal Biran
źródło
Po onTextChangedprostu potrzebuje public voidprzed sobą.
Andy
Dziękuję za komentarz. Naprawiony.
Eyal Biran
1

Zrobiłem coś takiego, aby było to proste:

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

W ten sposób zamieniamy wszystkie niechciane znaki w nowej części ciągu źródłowego na pusty ciąg.

edit_textZmienna jest EditTextobiektem mamy na myśli.

Kod jest napisany kotlin.

Lazar
źródło
1
Dzięki! To rozwiązanie sprawdza się zarówno podczas pisania, jak i wklejania tekstu.
CoolMind
0

Jest możliwe użycie setOnKeyListener. W tej metodzie możemy dostosować dane wejściowe edittext!

Võ Hoài Lên
źródło
0

W ten sposób utworzyłem filtr dla pola Nazwa w Edytuj tekst (pierwsza litera to WIELKIE LITERY i zezwalaj tylko na jedną spację po każdym słowie.

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
źródło
Constants.Delimiter.BLANK nie jest znana.
CoolMind
0

Taką samą odpowiedź mam w Kotlinie:

/**
 * 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
        }
    }
}

i pobierz klasę z funkcją rozszerzenia:

fun EditText.filterByDataType(filterType: Int) {
    this.filters = arrayOf<InputFilter>(InputFilterAlphanumeric(filterType))
}
Irving Kennedy
źródło