Developing Frogtek

El blog del Departamento de Tecnología

Etiqueta: edittext

Restringir uso de emoticonos en EditText

La nueva versión 4.4 del SDK de android (Kit Kat) trae consigo los famosos emoticonos (o emojis), añadidos al teclado software, que tanto gustan en whatsapp o facebook.

emoji

 

Nos hemos encontrado con algún usuario de nuestra aplicación que los ha utilizado para dar más colorido a los nombres de proveedores, productos, clientes…

Pero luego nos han dado problemas a la hora de hacer alguna consulta SQL con un LIKE comparando strings.

¿Cómo lo he solucionado? Si limito los EditText a únicamente letras del abecedario o números, estoy haciendo que la aplicación no pueda ser 100% funcional en países donde se usan otro tipo de caracteres como Rusia, Japón, China…
He decidido usar la clase InputFilter, como sigue:


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)) 
                && !Character.isWhitespace(source.charAt(i))) {
                return "";
            }
        }
        return null;
    }
};

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

Espero que te sirva. O si tienes una solución mejor, es bienvenida.

Hints en los EditText de Android

Esto va a ser un mini-post, pero me ha parecido interesante comentarlo ya que es algo de lo que hasta ahora, no me había dado cuenta.

Si estás programando en Android, seguramente te hayas encontrado con la siguiente situación:

Tienes un EditText vacío, que el usuario ha de completar, pero para darle más información sobre lo que debería ir en ese campo de texto, desearías poner un “hint”, una ayuda en forma de texto gris sobre el EditText.

Pero quieres que cuando se pinche sobre el campo editable, este texto desaparezca.

Se podría hacer todo por código, pero queda mucho más bonito y limpio un:

android:hint=”Introduzca Texto Aquí”

Simple y elegante.

El findViewById(), y lo cargante que puede llegar a ser

Si no vienes del post introductorio, quizás te gustaría leerlo.

Segundo asalto. La aplicación sigue estando lenta, y no sabemos muy bien por qué. Ya no hay tantos onMeasure(); sólo los “necesarios”. Pero hay una nueva función que aparece en los primeros puestos de nuestro querido traceview: findViewTraversal(). ¿Y quién llama tantísimas veces a esta última? pues findViewById().
Y es que esta función, es la que enlaza un recurso de la interfaz de usuario de una aplicación, con una variable en nuestro código. O simplemente se encarga de acceder a ese recurso para cambiar una de sus propiedades.

Supongamos, pues, que tenemos un layout con una veintena de widgets entre layouts, Buttons, EditText, TextView, ImageView… Y que en un momento dado del ciclo de vida de la actividad, tenemos que acceder a uno ó varios de ellos para cambiar sus propiedades. Podemos acceder a ellos del modo:
((EditText) this.findViewById(R.id.edittext_cliente)).setOnClickListener(…);
Aunque también podemos guardar el valor que devuelve la función findViewById() en una variable, o variable miembro de la Activity para operar con ella varias veces a lo largo del citado ciclo de vida.

¿Cuál es la diferencia? y ya que estamos ¿cuál era nuestro error? Pues que cada vez que se llama a la función findViewById(), android recorre todo el árbol jerárquico del Layout que hemos definido previamente en XML, en busca del recurso al que queremos acceder. Como ya habréis averiguado, el costo de esa operación es muy alto si el control a encontrar es de los últimos en el árbol, o si hay muchos niveles de layouts anidados, o simplemente queremos cambiar el valor de una de las propiedades de ese recurso bastantes veces a lo largo del ciclo de vida de la aplicación.
Si a esto le sumamos que el cambio en una de las propiedades de un EditText (por ejemplo), afecta a cinco o seis widgets más; y a todos accedemos del modo findViewById(), estamos vendidos.

La solución para una Activity, cuyos widgets han de ser modificados con bastante asiduidad es el guardar el resultado de la función archinombrada en este post en variables miembro, a las cuales se accederá sin tener que recorrer (findViewTraversal()) todo el árbol jerárquico “n” veces, con la consiguiente pérdida de velocidad en la transición de un estado a otro de la Activity.
Eso sí: Podemos prescindir de estas variables miembro si accedemos una, y sólo una, vez al widget en cuestión.

Esto también hay que grabarlo a fuego en la mente.

Diseño de layouts XML, sucios pero eficientes, en android

Si no vienes del post introductorio, quizás te gustaría leerlo.

Bien, pues a medida que añadíamos nueva funcionalidad a la aplicación en la tablet, experimentábamos una lentitud tremenda en alguna de las actividades que abríamos. No era normal que si en el teléfono, el mismo layout funcionaba perfectamente, ¿por qué al unir dos layouts en uno sólo, pero más grande, vaya tan lento? Respuesta: no hay nada que el traceview no solucione. Nada.

Rápidamente, Julio comenzó a ver que había miles de llamadas a una función nativa de android que se llama onMeasure(). La misma, se encarga de que cada vez que hay que posicionar un widget dentro del layout, hacer las medidas necesarias de lo ya presente en el mismo para así determinar dónde debe de ir colocado. ¿Y qué problema hay con esto? Pues que si tu layout tiene muchos widgets, y estos widgets tienen algunas de sus propiedades (como el layout_width, o layout_height) con valores wrap_content o fill_parent, todo resultará en un festival de onMeasure(). Creo recordar, que el 90% del tiempo de carga de una pantalla ineficiente nuestra, se lo llevaba esta función. Obviamente, pasamos de medio segundo para cargar una pantalla determinada en el teléfono, a 2 ó 3 (incluso más) en la tablet.
Por lo tanto, uno de los nuevos mandamientos que hay que grabarse a fuego es que: siempre darás medidas a tus widgets (Button, TextView, Layouts, EditText, etc), en lugar de que las decida android. Siempre, a no ser que sea estrictamente necesario; que alguna vez lo es.
Este es el primer paso para que la aplicación fluya un poquito más rápido que antes. Pero sólo un poquito.

¿Qué, que aún no hemos ensuciado el layout? Bien, pues este punto tiene que ver con el nivel en que un widget se encuentra anidado. Podemos tener un layout muy sencillo, con un RelativeLayout y dos Button dentro, o podemos tener algo como esto (o peor). Es por esto lo que un día dije en la oficina. Somos unos barrocos.
Por lo tanto, desde frogtek recomendamos encarecidamente que, a poder ser, se tenga un RelativeLayout padre con todos sus hijos al mismo nivel. Si se tienen 4 o 5 widgets en el layout, serás afortunado; si tienes 30 o 40 como en alguno de nuestra tablet, puedes volverte loco buscando uno en concreto en el Outline de eclipse. Cierto es que queda todo mucho más ordenado si vamos anidando widgets dentro de Layouts, pero se pierde rapidez al cargar, ya que también hay muchos más onMeasures() que si estuviesen todos al mismo nivel. Y traceview no miente.

Por lo tanto, el primer paso para agilizar la carga (o recarga) de una pantalla, pasa por dar medidas a todo widget al que podamos dar medidas absolutas, e intentar que nuestros layouts sean lo menos barrocos posibles en cuanto a ordenación jerárquica se refiere.

Consejo extra que se me ocurre mientras escribo: si utilizas el LayoutInflater para cargar layouts dentro de otros, tener un layout con uno o dos niveles de anidación ayudará mucho. Nosotros, que tenemos layouts dentro de layouts, y de nuevo dentro de layouts (por necesidad), lo hemos notado.

Optimizar EditText en modo landscape

Hoy me ha pasado algo mientras programaba una nueva pantalla, que me ha traído a la cabeza recuerdos de toda la vida de la UI de la aplicación. La verdad, ha cambiado y mejorado mucho desde su inicio. Cuando comenzamos hace mucho tiempo el desarrollo de nuestro producto, nos centramos únicamente en una versión para teléfonos android, sin pensar en otro tipo de dispositivos (no había más gadgets con este sistema que el HTC G1).
Desde ese momento con menor atractivo que funcionalidad, el diseño de nuestra interfaz gráfica se basaba en pantallas con layouts verticales. Obviamente, trabajábamos en el emulador que venía en las primeras versiones del SDK de android y aún no teníamos a varios clientes reales dando feedback sobre la usabilidad. Por aquel entonces, era más importante el ir implementando funcionalidad que hacer la aplicación preciosa; ya habría tiempo para ello más tarde.

Pero llegó el día en que algunas personas comenzaron a utilizar la aplicación, y a sugerir cosas nuevas para la interfaz gráfica. Alguna de ellas venía dado el uso que se hacía de la aplicación y que no tenía nada que ver con el modo al que la usábamos los programadores. Resulta que el HTC G1 tiene un teclado físico, que aparece al desplazar la pantalla; y al mostrar este teclado, si en nuestro fichero AndroidManifest.xml no se dice lo contrario, la pantalla cambia a modo landscape u horizontal. Por lo que la pantalla ha de tener su equivalente horizontal, o se pierde más o menos la mitad del los widgets que interaccionan con el usuario. Pero esa es otra historia.

Teníamos un problema al que no prestamos mucha atención, por no decir ninguna. Y es que, por defecto, cuando un EditText (o caja de texto) es seleccionado para introducir un texto y el teléfono está en modo landscape, la caja de texto viene a ocupar todo el espacio que no ocupa el teclado software de android (desde la SDK 1.6). Por lo que el usuario, puede perder la noción de en qué caja de texto se encuentra, si hay muchas de ellas en una determinada pantalla. Pero esto no era un gran problema en una pequeña pantalla como la del HTC G1, con una resolución de 380×420, no importa mucho que una caja de texto ocupe gran parte de la pantalla en modo landscape. ¿Y qué me ha pasado hoy? Pues que al desarrollar para una resolución de 800×480, tenemos una caja de texto en la que, por poco, no cabe la biblia en verso. Para muestra, un botón:
¿Un bonito EditText de 700 y pico pixeles de ancho por unos 240 de alto? ¡Ni hablar! Sobre todo, cuando se utiliza para una propiedad de un producto de una tienda como por ejemplo: el nombre.
¿Qué hacer? Pues investigar un poco en horario after hours. Resulta, que se puede modificar el comportamiento de un EditText una vez ha sido seleccionado para introducir texto. Esto se hace mediante la propiedad android:imeOptions. Sus posibles valores son distintos flags que modifican el comportamiento tando del EditText como del teclado software. El que me interesa es flagNoExtractUi; de este modo, al ganar el foco el EditText, muestra el teclado software (si queremos que se muestre), editamos directamente sobre la caja de texto, y no sobre la misma maximizada para escribir 15 carácteres a lo sumo para escribir el nombre como ya he dicho.

Así que hay veces, que es mejor perder tiempo leyendo documentación y posts del equipo de android, que ganar tiempo yendo rápido y no pensando en pequeños detalles.

Muy bien Pablo, pero ¿y si quiero mantener ese aspecto maximizado para que escriban, por ejemplo, la biblia en verso y como tengo tantos campos grandes no quiero que se pierdan por la UI al presionar la tecla Next que pasa el foco al siguiente EditText?
Pablo responde: Entonces tienes una propiedad del EditText llamada android:Hint, que muestra en color gris clarito un pequeño texto informativo definido por el programador. Este texto desaparecerá cuando el usuario comience a escribir, y volverá a aparecer cuando se vacíe el texto. Así el usuario podrá ver en la oscuridad de las UI de android.