Developing Frogtek

El blog del Departamento de Tecnología

Etiqueta: layout

Android y sus Layouts

Imaginad que al realizar una aplicación web compleja, en vez de hacerla para que se vea bien en todos los navegadores, la hacéis solo para una resolución de pantalla concreta y un navegador específico. Sería un infierno tener que repasarla entera, una vez acabada, para que se adapte a cualquier navegador y resolución, ¿verdad?.

Android Layout

Pues algo así nos pasó a nosotros, pero en Android. Parte de culpa tuvimos, he de reconocer, por no ser previsores y tirarnos por el camino fácil. Aunque diré en nuestra defensa que cuando se empezó nuestro producto Android estaba en pañales y no existía tanta tablet y teléfono en el cual ejecutar tu aplicación.

Aquí van pues una serie de recomendaciones. Para que no cometáis los mismos errores que nosotros.

  • Entended cómo funciona el tema de las pantallas en Android, no es lo mismo una densidad que otra, el tamaño de pantalla, etc.
  • No uséis la unidad de medida px. Mejor dp, o sp si son tamaños de texto.
  • Tened claro como elige Android los recursos que ponéis a su disponibilidad.
  • Evitad en toda medida posible usar layouts con medidas absolutas, hacedlos adaptables (fill_parent, wrap_content). Os ahorrareis muchos quebraderos de cabeza.
  • Si no podéis evitarlo y tenéis que usar valores en vuestros layouts. Extraedlos a un xml y definidlos como Dimensions. Así os será más fácil adaptar la aplicación a las distintas configuraciones que queráis soportar, pues con definir el archivo de dimensiones para cada modalidad de recursos os valdrá.
  • Usad el atributo android:drawableTop, android:drawableLeft…, para introducir imágenes en botones. No os pase como a nosotros que o pedíamos el fondo ya con la imagen o introducíamos un elemento adicional, recargando el layout.
  • No os dejéis enamorar por las bondades de los RelativeLayout. No son malos. Pero a veces un LinearLayout con sus pesos puede ser más fácilmente adaptado a varias pantallas.

Y nada más. Si se os ocurre alguna más que nosotros no hemos nombrado, compartidlo con todos!

 

Botones en Android mediante programación

El pasado jueves 7 de abril, la troupe de Frogtek acudimos en comandita al taller organizado por el GTUG Zaragoza e impartido por Francho Joven. La convocatoria fue un éxito y salimos todos muy contentos con lo aprendido y orgullosos de la aplicación que desarrollamos y subimos al market de Android.

Una de las lecciones que vimos fue la de asignar formas (o shapes) creadas mediante programación a elementos como botones. Y yo pensé: ¿por qué no podemos hacer eso mismo con los botones de nuestra aplicación?

Como ejemplo, pongamos que tenemos que añadir un botón de “regresar” típico, que al ser pulsado, vuelva a la pantalla anterior.

Lo primero es conseguir un Button con fondo rojo. Para ello, creamos un layout con un shape (en un fichero shape_red_background.xml) dentro de la carpeta layouts, que nos definirá un tipo de fondo que podremos aplicar a elementos rojos de nuestra aplicación. Le incluiremos un degradado para que quede más molón.



	
	
	
	

Después, queremos asignarle este fondo a nuestro botón, pero como queremos hacerlo reutilizable, vamos a definirlo en un estilo. Para ello, nos creamos un archivo style.xml dentro de nuestra carpeta values (el nombre es lo de menos, lo importante es que esté en values) y añadimos lo siguiente:




Notad que el parámetro android:drawableLeft es para que aparezca una flechica a la izquierda de este botón. Y ahora solo queda añadir este botón a nuestro layout. Para ello basta con:

Bastante limpio, ¿no? Y lo mejor de todo es que si mañana nos piden el mismo botón, pero en color verde, como hemos utilizado herencia de estilos, con hacer un nuevo shape de color verde y crear su style es suficiente:


Pensad ahora la cantidad de espacio que podemos ahorrarnos en nuestras aplicaciones si sustituimos los archivos de imagen por formas creadas mediante programación…

EDIT: de momento, lo único que no he conseguido es darle sombra mediante programación. Existen parámetros shadow_color en Android, pero solo funcionan para elementos de tipo texto.

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 una aplicación android (Introducción)

Hacía como mes y medio que quería escribir sobre este tema, pero me habría precipitado ya que desde esa fecha hasta ahora hemos aprendido mucho más sobre la optimización de una aplicación para android; aunque aún no quedan muchos conocimientos por adquirir.

Como ya hemos contado alguna vez, desde hace casi dos años, desarrollamos nuestro producto para teléfonos; teléfonos con una resolución fija. Y durante este verano, hemos dado el salto a una tablet. Suerte que todo el núcleo de la aplicación lo teníamos bien separado en un .jar. Por lo que el 2% del tiempo lo hemos invertido en retocar un poquito el núcleo, y el 95% restante ha sido armar una interfaz de usuario muy bonita, pero que nos ha dado bastantes quebraderos de cabeza, y que voy a intentar plasmar en una serie de posts.

Tras unas cuantas iteraciones y un empujón espectacular, teníamos muy avanzada la versión para la tablet. Pero el cambio de una pantalla de 320 x 480, a una de 800 x 480 conlleva problemas de memoria y de rendimiento (con un hardware superior al del teléfono, todo iba mucho más lento). Por que no es oro todo lo que reluce. Con un buen procesador, y bastante memoria no todo va a funcionar como pensabas. Por lo menos, en android. Nos hemos rascado mucho la cabeza, hemos sufrido, pero finalmente hemos aprendido muchas cosas; sobretodo de nuestros errores.

Es por ello, que nos gustaría compartir con vosotros los siguientes temas:

A medida que vayamos redactando la serie de posts, se actualizarán los puntos con enlaces a las URLs.

Usando estilos en Android

Ayer lo comentó Pablo en la oficina y no puedo estar más de acuerdo:

Cada día que pasa, estoy más convencido de estar programando CSS en lugar de android.

Programar la interfaz de usuario en Android tiene un cierto parecido a programar páginas webs.  Ya de por sí, en una aplicación Android, tenemos la vista separada del resto de la lógica, si usamos estilos en nuestros diseños, podremos además:

Reutilizar código: ¿Que es mejor? ¿Tener para cada elemento atributos que definan su estilo, atributos que posiblemente se repitan en elementos similares? ¿o crear un estilo que unifique todos esos atributos de forma que podamos reutilizarlos cada vez que queramos?

Tener un diseño homogéneo: ¿Os ha pasado de tener una aplicación en la que se tienen diferentes tamaños de letras para elementos similares? Seguramente en nuestra aplicación tengamos TextViewsEditTextsButtons y queremos que todos tengan la misma forma.

Usar estilos en Android es tan simple como:

-Crear un fichero .xml dentro de la carpeta values de nuestra aplicación. El nombre del fichero puede ser cualquiera, pero por claridad, podríamos llamarlo “style.xml”

-Dentro del fichero style.xml añadimos los diferentes bloques de estilos, muy parecido a lo que nos podemos encontrar en un fichero .css

  


    
    
     
     
    


Por otro lado, aplicaremos el estilo en el elemento que queramos.




De esta forma nos ahorramos muchas líneas de código.