Come posso utilizzare InputFilter per limitare i caratteri in un EditText in Android?


171

Voglio limitare i caratteri solo a 0-9, az, AZ e barra spaziatrice. Impostazione del tipo di input Posso limitare le cifre, ma non riesco a capire i modi in cui Inputfilter guarda i documenti.

Risposte:


186

L'ho trovato su un altro forum. Funziona come un campione.

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

58
in realtà non funziona altrettanto bene nei nuovi androidi (come 4.0+). Introducono suggerimenti di dizionario sopra la tastiera. Quando digiti una parola comune (diciamo "il") seguita da un carattere illegale per questo filtro (diciamo "-"), l'intera parola viene eliminata e dopo aver digitato un altro carattere (anche quelli consentiti, come "blah") il filtro restituisce "" e nessun carattere appare nel campo. Questo perché il metodo ottiene uno SpannableStringBuilder nel parametro di origine con "the-blah" e parametri di inizio / fine che coprono l'intera stringa di input ... Vedi la mia risposta per una soluzione migliore.
Łukasz Sromek,

4
In quell'esempio, dove restituisce "", penso che dovrebbe restituire il testo che dovrebbe essere visualizzato. cioè dovresti rimuovere i caratteri illegali e restituire la stringa che VUOI visualizzare. developer.android.com/reference/android/widget/… , android.view.KeyEvent)
Andrew Mackenzie

+1 Davvero un'ottima risposta. Character.isLetterOrDigit () tutti questi metodi sono molto utili.
Atul Bhardwaj,

@AndrewMackenzie Se il carattere di input, ad esempio, una virgola- ',', che non è legale, e voglio rimuoverlo, come riparare il codice sopra.
twlkyao,

! Character.isLetterOrDigit (source.charAt (i)) &&! Character.isSpaceChar (source.charAt (i)) per consentire gli spazi
Leon,

139

InputFiltersono un po 'complicati nelle versioni di Android che mostrano suggerimenti nel dizionario. A volte si ottiene un SpannableStringBuilder, a volte un chiaro Stringnel sourceparametro.

Quanto segue InputFilterdovrebbe funzionare. Sentiti libero di migliorare questo codice!

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

2
c'è un motivo per cui non vorrai sottoordinare la fonte? Vedi qualcosa di sbagliato nel fare questo (per consentire solo caratteri alfanumerici più alcuni caratteri speciali): String replacement = source.subSequence(start, end).toString(); return replacement.replaceAll("[^A-Za-z0-9_\\-@]", "");
Splash

3
Ciò non tiene conto di un problema in cui viene visualizzato il testo del suggerimento del dizionario ripetuto. @serwus lo ha identificato nella loro risposta. Fondamentalmente dovresti restituire null se non vengono apportate modifiche in entrambi i casi.
hooby3dfx,

3
Questo codice funziona perfettamente come ha detto lukasz (nella risposta sopra) ma sto affrontando qualche problema con le parole non di dizionario. quando scrivo chiru mostra 3 volte come chiruchiruchiru. come risolverlo? e occupa anche spazi bianchi ma come limitare gli spazi bianchi successivi?
Chiru,

3
Per qualche motivo, quando source instanceof SpannableStringBuilderentrare in AB mi dà AAB come quando provo la risposta precedente. Fortunatamente sono stato in grado di aggirare il problema utilizzando la soluzione @florian di seguito.
Guillaume,

1
@ hooby3dfx ha assolutamente ragione. Anche se alla fine riesce a ottenere la stringa giusta, si incasina quando si utilizza il completamento di dizionario / parola.
Aravind,

107

molto più facile:

<EditText
    android:inputType="text"
    android:digits="0,1,2,3,4,5,6,7,8,9,*,qwertzuiopasdfghjklyxcvbnm" />

9
Mentre questa sembra una risposta perfetta, c'è un problema secondo i documenti: "Come per tutte le implementazioni di KeyListener, questa classe riguarda solo le tastiere hardware. I metodi di input software non hanno l'obbligo di attivare i metodi in questa classe." Penso che un InputFilter sia probabilmente un modo migliore, sebbene più complicato, di procedere.
Craig B,

19
Soluzione fantastica Voglio solo aggiungere Non è necessario cedere ",". Puoi usare qualcosa del genere"0123456789qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM"
DeltaCap019,

26
Non è una soluzione multilingue
AAverin il

Se prevedi di utilizzare le traduzioni nella tua app. NON USARE QUESTA SOLUZIONE!
grantespo,

Sembra che potrebbe non funzionare con questo imeOptions="actionNext", ecc.
Pete Doyle,

68

Nessuna delle risposte postate ha funzionato per me. Sono venuto con la mia soluzione:

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

3
Questa è l'unica risposta che in realtà ha l'approccio giusto per evitare di ripetere il testo dai suggerimenti del dizionario! Upvote!
hooby3dfx,

4
L'unica cosa EditTextpuò già avere i propri filtri, ad es. Filtro di lunghezza. Quindi, invece di sovrascrivere i filtri, molto probabilmente desideri aggiungere i filtri a quelli già esistenti.
Aleks N.

È ancora aggiornato? Per me Android 6.0.1 funziona sulla tastiera su schermo
XxGoliathusxX,

2
Un problema minore con questa risposta (o con il funzionamento del meccanismo di input di Android) è che il backspace a volte sembra che l'utente non stia funzionando perché sta eseguendo il backspacing su caratteri non validi precedentemente inseriti che sono ancora presenti nel buffer di origine.
jk7,

1
Stesso problema con la soluzione di Łukasz Sromek: un carattere non valido che segue uno spazio viene sostituito da uno spazio.
Ingo Schalk-Schupp,

25

Usa questo suo lavoro al 100% le tue necessità e molto semplice.

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

In strings.xml

<string name="myAlphaNumeric">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</string>

15

Per evitare caratteri speciali nel tipo di input

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

Puoi impostare il filtro sul testo di modifica come di seguito

edtText.setFilters(new InputFilter[] { filter });

@sathya Wc, felice di aiutarti :)

1
Non blocca nessuno di questi personaggi. ㋡ ㋛ ☺ ☹ ☻ 〠シッツヅÜ 〲 〴 ϡ ٿ ت ⍡ ⍢ ⍣
Anarchofascist

7

Oltre alla risposta accettata, è anche possibile utilizzare ad esempio: android:inputType="textCapCharacters"come attributo di <EditText>per accettare solo caratteri maiuscoli (e numeri).


5
android: inputType = "textCapCharacters" non si limita all'utilizzo di altri caratteri come '., - "ecc.
Tvd

Inoltre è solo un suggerimento per il metodo di input. Non limita quali caratteri possono essere immessi.
dc

5

Per qualche motivo il costruttore della classe android.text.LoginFilter è di tipo package, quindi non è possibile estenderlo direttamente (anche se sarebbe identico a questo codice). Ma puoi estendere LoginFilter.UsernameFilterGeneric! Quindi hai solo questo:

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

Questo non è davvero documentato, ma fa parte della libreria principale e la fonte è semplice . Lo sto usando da un po 'di tempo, finora nessun problema, anche se ammetto di non aver provato a fare qualcosa di complesso che coinvolge spannables.


5

È giusto, il modo migliore per risolverlo nel layout XML stesso usando:

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

come giustamente indicato da Florian Fröhlich, funziona bene anche per le visualizzazioni di testo.

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

Solo una parola di cautela, i personaggi citati in android:digitsverranno visualizzati solo, quindi fai attenzione a non perdere nessun set di caratteri :)


devi definire inputType = "textFilter", solo allora funzionerà correttamente.
Shreyash Mahajan,

@ShreyashMahajan, dipende dall'app per dispositivo / tastiera? Nel mio caso inputTypenon influisce sul filtro.
CoolMind il

4

Questa semplice soluzione ha funzionato per me quando avevo bisogno di impedire all'utente di inserire stringhe vuote in un EditText. Ovviamente puoi aggiungere più personaggi:

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

}

come chiamare questa soluzione?
zeek

1

Se si esegue la sottoclasse di InputFilter, è possibile creare il proprio InputFilter che filtra qualsiasi carattere non alfanumerico.

L'interfaccia InputFilter ha un metodo filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend)e ti fornisce tutte le informazioni che devi sapere su quali caratteri sono stati inseriti nel EditText a cui è assegnato.

Dopo aver creato il tuo InputFilter, puoi assegnarlo a EditText chiamando setFilters (...).

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


1

Ignorando le cose di span che altre persone hanno affrontato, per gestire correttamente i suggerimenti del dizionario ho trovato che il codice seguente funziona.

La fonte cresce man mano che cresce il suggerimento, quindi dobbiamo guardare a quanti personaggi ci si aspetta che sostituiamo prima di restituire qualcosa.

Se non abbiamo caratteri non validi, restituire null in modo che si verifichi la sostituzione predefinita.

Altrimenti dobbiamo estrarre i caratteri validi dalla sottostringa che REALMENTE verrà posizionata nel 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;
    } 
}; 

1

per impedire le parole in edittext. crea una classe che puoi usare in qualsiasi momento.

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

1

Per prima cosa aggiungi in strings.xml:

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

XML :

android:digits="@string/vin_code_mask"

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

Strano, ma funziona in modo strano sulla tastiera morbida dell'emulatore.

Avvertimento! Il codice seguente filtra tutte le lettere e gli altri simboli tranne le cifre per le tastiere del software. Sullo smartphone apparirà solo la tastiera digitale.

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

Ho anche di solito impostato maxLength, filters, inputType.


1

Questo è un vecchio thread, ma le soluzioni proposte hanno tutti problemi (a seconda del dispositivo / versione Android / Tastiera).

APPROCCIO DIVERSO

Quindi alla fine sono andato con un approccio diverso, invece di usare l' InputFilterimplementazione problematica, sto usando TextWatchere TextChangedListeneril EditText.

CODICE COMPLETO (ESEMPIO)

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

Il motivo InputFilternon è una buona soluzione in Android perché dipende dall'implementazione della tastiera. L'ingresso della tastiera viene filtrato prima che l'input venga passato a EditText. Ma poiché alcune tastiere hanno implementazioni diverse per l' InputFilter.filter()invocazione, questo è problematico.

D'altra parte TextWatchernon si preoccupa dell'implementazione della tastiera, ci consente di creare una soluzione semplice ed essere sicuri che funzionerà su tutti i dispositivi.


Il onTextChangedbisogno semplicemente di un public voidfronte in esso.
Andy,

Grazie per il commento. Fisso.
Eyal Biran,

1

Ho fatto qualcosa del genere per renderlo semplice:

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

In questo modo stiamo sostituendo tutti i caratteri indesiderati nella nuova parte della stringa di origine con una stringa vuota.

La edit_textvariabile è l' EditTextoggetto a cui ci riferiamo.

Il codice è scritto in kotlin.


1
Grazie! Questa soluzione funziona bene quando si digita e si incolla un testo.
CoolMind

0

E 'possibile utilizzare setOnKeyListener. In questo metodo, possiamo personalizzare l'input edittext!


0

È così che ho creato il filtro per il campo Nome in Modifica testo (la prima lettera è MAIUSCOLO e consente solo uno spazio dopo ogni parola.

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

Constants.Delimiter.BLANK non è noto.
CoolMind il

0

Ho la stessa risposta 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
        }
    }
}

e ottieni la classe con una funzione Extension:

fun EditText.filterByDataType(filterType: Int) {
    this.filters = arrayOf<InputFilter>(InputFilterAlphanumeric(filterType))
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.