¿Cómo uso InputFilter para limitar los caracteres en un EditText en Android?

171

Quiero restringir los caracteres a 0-9, az, AZ y barra espaciadora solamente. Al establecer el tipo de entrada, puedo limitarlo a dígitos, pero no puedo entender las formas en que Inputfilter mira a través de los documentos.

Tim Wayne
fuente

Respuestas:

186

Encontré esto en otro foro. Funciona como un campeón.

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 });
queso claro de luna
fuente
58
en realidad no funciona tan bien en los androides más nuevos (como 4.0+). Presentan sugerencias de diccionario sobre el teclado. Cuando escribe una palabra común (digamos "el") seguido de un carácter ilegal para este filtro (digamos, "-"), la palabra completa se elimina y después de escribir otros caracteres (incluso los permitidos, como "bla") El filtro devuelve "" y no aparece ningún carácter en el campo. Esto se debe a que el método obtiene un SpannableStringBuilder en el parámetro fuente con "the-blah" y parámetros de inicio / finalización que abarcan toda la cadena de entrada ... Vea mi respuesta para una mejor solución.
Łukasz Sromek
44
En ese ejemplo, donde devuelve "", creo que debería devolver el texto que debería mostrarse. es decir, debe eliminar los caracteres ilegales y devolver la cadena que DESEA mostrada. developer.android.com/reference/android/widget/… , android.view.KeyEvent)
Andrew Mackenzie
+1 Realmente una gran respuesta. Character.isLetterOrDigit () todos estos métodos son muy útiles.
Atul Bhardwaj
@AndrewMackenzie Si el carácter de entrada, por ejemplo, una coma- ',', que no es legal, y quiero eliminarlo, cómo solucionar el código anterior.
twlkyao
! Character.isLetterOrDigit (source.charAt (i)) &&! Character.isSpaceChar (source.charAt (i)) para permitir espacios
Leon
139

InputFilterLos s son un poco complicados en las versiones de Android que muestran sugerencias de diccionario. A veces se obtiene un SpannableStringBuilder, a veces un plano Stringen el sourceparámetro.

Lo siguiente InputFilterdebería funcionar. ¡Siéntase libre de mejorar 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
fuente
2
¿hay alguna razón por la que no quieras subsecuenciar la fuente? ¿Usted ve mal cualquier cosa con sólo hacer esto (con el fin de permitir sólo caracteres alfanuméricos además de algunos caracteres especiales): String replacement = source.subSequence(start, end).toString(); return replacement.replaceAll("[^A-Za-z0-9_\\-@]", "");
Splash
3
Esto no tiene en cuenta un problema en el que aparece el texto de sugerencia de diccionario repetido. @serwus identificó eso en su respuesta. Básicamente, debe devolver nulo si no se realizan modificaciones en ambos casos.
hooby3dfx
3
Este código funciona perfecto como dijo lukasz (en la respuesta anterior) pero estoy enfrentando algún problema con las palabras que no están en el diccionario. cuando escribo chiru se muestra 3 veces como chiruchiruchiru. ¿Cómo resolverlo? y también toma espacios en blanco, pero ¿cómo restringir los próximos espacios en blanco?
chiru
3
Por alguna razón, cuando source instanceof SpannableStringBuilderingresar AB me da AAB como cuando intento la respuesta anterior. Afortunadamente, pude solucionarlo usando la solución @florian a continuación.
Guillaume
1
@ hooby3dfx tiene toda la razón. Si bien finalmente logra hacer que la cadena sea correcta, se arruina cuando usa la finalización de diccionario / palabra.
Aravind
107

más fácil:

<EditText
    android:inputType="text"
    android:digits="0,1,2,3,4,5,6,7,8,9,*,qwertzuiopasdfghjklyxcvbnm" />
Florian Fröhlich
fuente
9
Si bien esto parece una respuesta perfecta, hay un problema según los documentos: "En cuanto a todas las implementaciones de KeyListener, esta clase solo se refiere a los teclados de hardware. Los métodos de entrada de software no tienen la obligación de activar los métodos en esta clase". Creo que un InputFilter es probablemente un camino mejor, aunque más complicado.
Craig B
19
Solución impresionante Solo desea agregar No necesita ceder ","en el medio. Puede usar algo como esto"0123456789qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM"
DeltaCap019
26
No es una solución multilingüe
AAverin
Si planeas usar traducciones en tu aplicación. ¡NO USE ESTA SOLUCIÓN!
grantespo
Parece que esto podría no funcionar imeOptions="actionNext", etc.
Pete Doyle
68

Ninguna de las respuestas publicadas funcionó para mí. Vine con mi propia solución:

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
fuente
3
¡Esta es la única respuesta que realmente tiene el enfoque correcto para evitar la repetición de texto de las sugerencias del diccionario! ¡Votación a favor!
hooby3dfx
44
Lo único, EditTextya puede tener sus propios filtros, por ejemplo, filtro de longitud. Entonces, en lugar de anular los filtros, lo más probable es que desee agregar sus filtros a los ya existentes.
Aleks N.
¿Esto todavía está actualizado? Para mí Android 6.0.1 funciona en el teclado en pantalla
XxGoliathusxX
2
Un problema menor con esta respuesta (o con el funcionamiento del mecanismo de entrada de Android) es que, a veces, el usuario cree que el espacio de retroceso no funciona porque está retrocediendo sobre caracteres no válidos ingresados ​​previamente que aún se encuentran en el búfer de origen.
jk7
1
El mismo problema que con la solución de Łukasz Sromek: un carácter no válido después de un espacio se reemplaza por un espacio.
Ingo Schalk-Schupp
25

Utilice este su trabajo 100% su necesidad y muy simple.

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

En strings.xml

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

Para evitar caracteres especiales en el 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;
    }
};

Puede configurar el filtro para su texto de edición como a continuación

edtText.setFilters(new InputFilter[] { filter });
CommonGuy
fuente
@sathya You wc, encantado de ayudarte :)
1
No bloquea ninguno de estos personajes. Arch ㋛ ☺ ☹ ☻ 〠 シ ッ ツ ヅ Ü 〲 〴 ϡ ﭢ ت ⍡ ⍢ ⍣ ⍤ ⍥ ⍨ ⍩ ὃ ὕ ὣ Ѷ
Anarchofascist 05 de
7

Además de la respuesta aceptada, también es posible usar, por ejemplo: android:inputType="textCapCharacters"como un atributo de <EditText>, para aceptar solo letras mayúsculas (y números).

mblenton
fuente
55
android: inputType = "textCapCharacters" no se limita al uso de otros caracteres como '., - "etc.
Tvd
También es solo una pista del método de entrada. No restringe los caracteres que se pueden ingresar.
dcow
5

Por alguna razón, el constructor de la clase android.text.LoginFilter tiene un alcance de paquete, por lo que no puede extenderlo directamente (aunque sería idéntico a este código). ¡Pero puede extender LoginFilter.UsernameFilterGeneric! Entonces solo tienes esto:

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

Esto no está realmente documentado, pero es parte de la biblioteca principal y la fuente es sencilla . Lo he estado usando durante un tiempo, hasta ahora no hay problemas, aunque admito que no he intentado hacer nada complejo que involucre spannables.

Groxx
fuente
5

Es correcto, la mejor manera de solucionarlo en el propio diseño XML utilizando:

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

como acertadamente señaló Florian Fröhlich, funciona bien incluso para vistas de texto.

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

Solo una advertencia, los caracteres mencionados en el android:digitssolo se mostrarán, así que tenga cuidado de no perder ningún conjunto de caracteres :)

Kailas
fuente
necesita definir inputType = "textFilter", entonces solo funcionará correctamente.
Shreyash Mahajan
@ShreyashMahajan, ¿depende de la aplicación del dispositivo / teclado? En mi caso inputTypeno afecta el filtrado.
CoolMind
4

Esta solución simple funcionó para mí cuando necesitaba evitar que el usuario ingresara cadenas vacías en un EditText. Por supuesto, puede agregar más personajes:

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
fuente
¿Cómo llamar a esta solución?
zeeks
1

Si subclasifica InputFilter, puede crear su propio InputFilter que filtraría los caracteres no alfanuméricos.

La interfaz InputFilter tiene un método, filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) y le proporciona toda la información que necesita para saber qué caracteres se ingresaron en el EditText al que está asignado.

Una vez que haya creado su propio InputFilter, puede asignarlo al EditText llamando a setFilters (...).

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

nicholas.hauschild
fuente
1

Ignorando las cosas de la extensión con las que otras personas se han ocupado, para manejar adecuadamente las sugerencias del diccionario, encontré que el siguiente código funciona.

La fuente crece a medida que crece la sugerencia, por lo que tenemos que ver cuántos caracteres realmente espera que reemplacemos antes de devolver cualquier cosa.

Si no tenemos caracteres no válidos, devuelva nulo para que ocurra el reemplazo predeterminado.

De lo contrario, debemos extraer los caracteres válidos de la subcadena que REALMENTE se colocará en 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
fuente
1

para evitar palabras en edittext. crea una clase que puedas usar en cualquier 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
fuente
1

Primero agregue a strings.xml:

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

XML :

android:digits="@string/vin_code_mask"

Código en Kotlin:

edit_text.filters += InputFilter { source, start, end, _, _, _ ->
    val mask = getString(R.string.vin_code_mask)
    for (i in start until end) {
        if (!mask.contains(source[i])) {
            return@InputFilter ""
        }
    }
    null
}

Es extraño, pero funciona de forma extraña en el teclado suave del emulador.

¡Advertencia! El siguiente código filtrará todas las letras y otros símbolos, excepto los dígitos para los teclados de software. Solo el teclado digital aparecerá en los teléfonos inteligentes.

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

Asimismo, establecer por lo general maxLength, filters, inputType.

CoolMind
fuente
1

Este es un hilo antiguo, pero las soluciones propuestas tienen problemas (dependiendo del dispositivo / versión de Android / Teclado).

ENFOQUE DIFERENTE

Así que finalmente fui con un enfoque diferente, en lugar de usar la InputFilterimplementación problemática, estoy usando TextWatchery el TextChangedListenerde EditText.

CÓDIGO COMPLETO (EJEMPLO)

editText.addTextChangedListener(new TextWatcher() {

    @Override
    public void afterTextChanged(Editable editable) {
        super.afterTextChanged(editable);

        String originalText = editable.toString();
        int originalTextLength = originalText.length();
        int currentSelection = editText.getSelectionStart();

        // Create the filtered text
        StringBuilder sb = new StringBuilder();
        boolean hasChanged = false;
        for (int i = 0; i < originalTextLength; i++) {
            char currentChar = originalText.charAt(i);
            if (isAllowed(currentChar)) {
                sb.append(currentChar);
            } else {
                hasChanged = true;
                if (currentSelection >= i) {
                    currentSelection--;
                }
            }
        }

        // If we filtered something, update the text and the cursor location
        if (hasChanged) {
            String newText = sb.toString();
            editText.setText(newText);
            editText.setSelection(currentSelection);
        }
    }

    private boolean isAllowed(char c) {
        // TODO: Add the filter logic here
        return Character.isLetter(c) || Character.isSpaceChar(c);
    }
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        // Do Nothing
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // Do Nothing
    }
});

La razón por la InputFilterque no es una buena solución en Android es que depende de la implementación del teclado. La entrada del teclado se está filtrando antes de pasar la entrada al EditText. Pero, dado que algunos teclados tienen implementaciones diferentes para la InputFilter.filter()invocación, esto es problemático.

Por otro lado, TextWatcherno le importa la implementación del teclado, nos permite crear una solución simple y asegurarnos de que funcione en todos los dispositivos.

Eyal Biran
fuente
El onTextChangedsimplemente necesita un public voidfrente.
Andy
Gracias por el comentario. Fijo.
Eyal Biran
1

He hecho algo como esto para que sea simple:

edit_text.filters = arrayOf(object : InputFilter {
    override fun filter(
        source: CharSequence?,
        start: Int,
        end: Int,
        dest: Spanned?,
        dstart: Int,
        dend: Int
    ): CharSequence? {
        return source?.subSequence(start, end)
            ?.replace(Regex("[^A-Za-z0-9 ]"), "")
    }
})

De esta manera, estamos reemplazando todos los caracteres no deseados en la nueva parte de la cadena de origen con una cadena vacía.

La edit_textvariable es el EditTextobjeto al que nos referimos.

El código está escrito en kotlin.

Lazar
fuente
1
¡Gracias! Esta solución funciona bien al escribir y pegar un texto.
CoolMind
0

Es posible de usar setOnKeyListener. ¡En este método, podemos personalizar la entrada edittext!

Võ Hoài Lên
fuente
0

Así es como creé el filtro para el campo Nombre en Editar texto. (La primera letra es MAYÚSCULAS, y permite solo un espacio después de cada palabra.

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
fuente
Constantes.Delimiter.BLANK es desconocido.
CoolMind
0

Tengo la misma respuesta en 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
        }
    }
}

y obtener la clase con una función de extensión:

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