Como uso o InputFilter para limitar caracteres em um EditText no Android?

171

Quero restringir os caracteres a 0-9, az, AZ e barra de espaço. Definindo o tipo de entrada Eu posso limitar os dígitos, mas não consigo descobrir as maneiras de o Inputfilter examinar os documentos.

Tim Wayne
fonte

Respostas:

186

Encontrei isso em outro fórum. Funciona como um campeão.

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 });
moonlightcheese
fonte
58
na verdade, ele não funciona tão bem em Androids mais recentes (como 4.0+). Eles introduzem sugestões de dicionário acima do teclado. Quando você digita uma palavra comum (digamos "the") seguida por um caractere ilegal para este filtro (digamos "-"), a palavra inteira é excluída e depois que você digita outros caracteres (mesmo os permitidos, como "blá") O filtro retorna "" e nenhum caractere aparece no campo. Isso ocorre porque o método obtém um SpannableStringBuilder no parâmetro de origem com "the-blah" e os parâmetros de início / fim que abrangem toda a cadeia de entrada ... Veja minha resposta para uma solução melhor.
Łukasz Sromek
4
Nesse exemplo, onde retorna "", acho que deve retornar o texto que deve ser exibido. ou seja, você deve remover os caracteres ilegais e retornar a string que deseja exibir. developer.android.com/reference/android/widget/... , android.view.KeyEvent)
Andrew Mackenzie
+1 Realmente uma ótima resposta. Character.isLetterOrDigit () todos esses métodos são muito úteis.
Atul Bhardwaj
@AndrewMackenzie Se o caractere de entrada, por exemplo, uma vírgula- ',', que não é legal, e eu quero removê-lo, como consertar o código acima.
Twlkyao 15/05
! Character.isLetterOrDigit (source.charAt (i)) && Character.isSpaceChar (source.charAt (i)) para permitir espaços!
Leon
139

InputFilters são um pouco complicados nas versões do Android que exibem sugestões de dicionário. Você às vezes recebe um SpannableStringBuilder, às vezes uma planície Stringno sourceparâmetro

O seguinte InputFilterdeve funcionar. Sinta-se livre para melhorar este código!

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
fonte
2
existe uma razão para você não querer subsequentemente a fonte? Você vê algo errado em fazer isso (para permitir apenas alfanuméricos e alguns caracteres especiais): String replacement = source.subSequence(start, end).toString(); return replacement.replaceAll("[^A-Za-z0-9_\\-@]", "");
Splash
3
Isso não leva em conta um problema em que o texto de sugestão de dicionário repetido aparece. @serwus identificou isso em sua resposta. Basicamente, você deve retornar nulo se nenhuma modificação for feita nos dois casos.
hooby3dfx
3
Esse código funciona perfeitamente como o lukasz disse (na resposta acima), mas estou enfrentando algum problema com as palavras que não são do dicionário. quando digito chiru, mostra 3 vezes como chiruchiruchiru. como resolver isso? e também leva espaços em branco, mas como restringir os próximos espaços em branco?
chiru
3
Por alguma razão, quando source instanceof SpannableStringBuilderdigitar AB me dá AAB, como ao tentar a resposta anterior. Felizmente, consegui contornar o problema usando a solução @florian abaixo.
Guillaume
1
@ hooby3dfx está absolutamente certo. Embora ele consiga acertar a sequência eventualmente, ela falha quando você usa a conclusão de dicionário / palavra.
Aravind
107

muito facil:

<EditText
    android:inputType="text"
    android:digits="0,1,2,3,4,5,6,7,8,9,*,qwertzuiopasdfghjklyxcvbnm" />
Florian Fröhlich
fonte
9
Embora isso pareça uma resposta perfeita, existe um problema de acordo com os documentos: "Como em todas as implementações do KeyListener, esta classe se preocupa apenas com teclados de hardware. Os métodos de entrada de software não têm obrigação de acionar os métodos dessa classe". Eu acho que um InputFilter é provavelmente um caminho melhor, embora mais complicado.
Craig B
19
Solução impressionante Só quero adicionar Você não precisa ceder ",". Você pode usar algo como isto"0123456789qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM"
DeltaCap019
26
Não é uma solução multilíngue
AAverin
Se você planeja usar traduções em seu aplicativo. NÃO USE ESTA SOLUÇÃO!
grantespo
Parece que isso pode não funcionar imeOptions="actionNext", etc.
Pete Doyle
68

Nenhuma das respostas postadas funcionou para mim. Eu vim com minha própria solução:

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
fonte
3
Esta é a única resposta que realmente tem a abordagem correta para evitar a repetição de texto de sugestões de dicionário! Voto a favor!
hooby3dfx
4
A única coisa EditTextque já pode ter seus próprios filtros, por exemplo, filtro de comprimento. Portanto, em vez de substituir os filtros, você provavelmente deseja adicionar seus filtros aos já existentes.
11274 Aleks N.Nov
Isso ainda está atualizado? Para mim Android 6.0.1 funciona no teclado na tela
XxGoliathusxX
2
Um pequeno problema com esta resposta (ou com a forma como o mecanismo de entrada do Android funciona) é que o backspace às vezes parece que o usuário não está funcionando, porque eles estão retornando sobre caracteres inválidos inseridos anteriormente que ainda estão retidos no buffer de origem.
Jk7 25/04
1
Mesmo problema da solução de Łukasz Sromek: Um caractere inválido após um espaço é substituído por um espaço.
Ingo Schalk-Schupp
25

Use isso seu trabalho 100% da sua necessidade e muito simples.

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

Em strings.xml

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

Para evitar caracteres especiais no tipo de entrada

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

Você pode definir o filtro para o seu texto de edição, como abaixo

edtText.setFilters(new InputFilter[] { filter });
CommonGuy
fonte
@sathya Você wc, feliz para ajudá-lo :)
1
Não bloqueia nenhum desses caracteres. Arch ㋛ ☺ ☹ 〠 シ ッ ツ Ü 〲 〴 ﭢ ت ⍡ ⍢ ⍣ ⍤ ⍨ ⍩ ὃ ὕ ὣ
Anarchofascist
7

Além da resposta aceita, também é possível usar, por exemplo: android:inputType="textCapCharacters"como um atributo de <EditText>, para aceitar apenas caracteres maiúsculos (e números).

mblenton
fonte
5
android: inputType = "textCapCharacters" não se restringe ao uso de outros caracteres como '., - "etc.
Tvd
Também é apenas uma dica para o método de entrada. Não restringe quais caracteres podem ser inseridos.
Dcow 01/10
5

Por alguma razão, o construtor da classe android.text.LoginFilter tem o escopo do pacote, portanto você não pode estendê-lo diretamente (mesmo que seja idêntico a esse código). Mas você pode estender o LoginFilter.UsernameFilterGeneric! Então você apenas tem isso:

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

Isso não está realmente documentado, mas faz parte da lib principal e a fonte é direta . Eu o uso há algum tempo, até agora sem problemas, embora admita que não tentei fazer nada complexo envolvendo spannables.

Groxx
fonte
5

É correto, a melhor maneira de corrigi-lo no próprio layout XML usando:

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

como corretamente apontado por Florian Fröhlich, ele funciona bem para exibições de texto.

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

Apenas uma palavra de cautela, os caracteres mencionados no android:digitsserão exibidos apenas, portanto, tenha cuidado para não perder nenhum conjunto de caracteres :)

Kailas
fonte
você precisa definir inputType = "textFilter" e somente ele funcionará corretamente.
Shreyash Mahajan
@ShreyashMahajan, isso depende do aplicativo do dispositivo / teclado? No meu caso inputType, não afeta a filtragem.
CoolMind
4

Essa solução simples funcionou para mim quando eu precisei impedir o usuário de inserir cadeias vazias em um EditText. Obviamente, você pode adicionar mais caracteres:

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
fonte
como chamar esta solução?
zeeks
1

Se você subclassificar InputFilter, poderá criar seu próprio InputFilter que filtraria quaisquer caracteres não alfanuméricos.

A Interface InputFilter possui um método filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend)e fornece todas as informações necessárias sobre quais caracteres foram inseridos no EditText ao qual está atribuído.

Depois de criar seu próprio InputFilter, você pode atribuí-lo ao EditText chamando setFilters (...).

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

nicholas.hauschild
fonte
1

Ignorando as coisas de extensão com as quais outras pessoas lidaram, para lidar adequadamente com as sugestões de dicionário, achei o seguinte código funcionando.

A fonte cresce à medida que a sugestão aumenta, por isso temos que ver quantos caracteres ele realmente espera que substituamos antes de retornar alguma coisa.

Se não tivermos caracteres inválidos, retorne nulo para que a substituição padrão ocorra.

Caso contrário, precisamos extrair os caracteres válidos da substring que REALMENTE será colocada no 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
fonte
1

para impedir palavras no edittext. crie uma classe que você possa usar a qualquer 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;
    }
}
Sahar Millis
fonte
1

Primeiro adicione em strings.xml:

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

XML :

android:digits="@string/vin_code_mask"

Código em 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
}

Estranho, mas funciona estranhamente no teclado virtual do emulador.

Aviso! O código a seguir filtrará todas as letras e outros símbolos, exceto os dígitos dos teclados de software. Somente o teclado digital aparecerá nos smartphones.

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

Eu também geralmente fixados maxLength, filters, inputType.

CoolMind
fonte
1

Esse é um tópico antigo, mas as soluções propostas têm problemas (dependendo do dispositivo / versão do Android / teclado).

ABORDAGEM DIFERENTE

Então, eventualmente, eu segui uma abordagem diferente, em vez de usar a InputFilterimplementação problemática, estou usando TextWatchere TextChangedListenera EditText.

CÓDIGO COMPLETO (EXEMPLO)

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

O motivo InputFilternão é uma boa solução no Android, pois depende da implementação do teclado. A entrada do teclado está sendo filtrada antes de ser passada para o EditText. Porém, como alguns teclados têm implementações diferentes para a InputFilter.filter()chamada, isso é problemático.

Por outro lado, TextWatchernão se preocupa com a implementação do teclado, pois permite criar uma solução simples e garantir que funcione em todos os dispositivos.

Eyal Biran
fonte
O onTextChangedsimplesmente precisa de um public voidna frente dele.
Andy
Obrigado pelo comentário. Fixo.
Eyal Biran
1

Fiz algo assim para simplificar:

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

Dessa forma, estamos substituindo todos os caracteres indesejados na nova parte da cadeia de origem por uma cadeia vazia.

A edit_textvariável é aEditText objeto ao qual estamos nos referindo.

O código está escrito kotlin.

Lazar
fonte
1
Obrigado! Esta solução funciona bem ao digitar e colar um texto.
CoolMind
0

É possível usar setOnKeyListener. Neste método, podemos personalizar a entrada edittext!

Võ Hoài Lên
fonte
0

Foi assim que criei um filtro para o campo Nome em Editar texto. (A primeira letra é CAPS e permite apenas um espaço após cada palavra.

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
fonte
Constants.Delimiter.BLANK é desconhecido.
CoolMind
0

Eu tenho a mesma resposta em 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 obtenha a classe com a função Extension:

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