Interfaces Auto-Adaptativas

“Desarrollo de Aplicaciones Móviles en Android”
Ejercicio Básico E: Interfaces Auto-Adaptativas


Con este ejercicio, ofrecemos algunas ideas de cómo solucionar el problema de presentar nuestras aplicaciones en los diferentes dispositivos existentes y con su sistema operativo Android correspondiente.
A. Descripción
A partir de la versión 1.6, el SDK ya permite que diseñemos interfaces distintas para cada tipo de pantalla. Éstas se dividen en tres grupos, baja resolución (240x320), media (320x480) y alta (480x800) con pequeñas variaciones. Podemos programar layouts diferentes para cada una de ellas, y para cada una de sus variaciones (por ejemplo, alta resolución de
480x854), pero lo mejor es que nuestras aplicaciones puedan defenderse más o menos por sí mismas, sin que nos estemos preocupando en exceso. Para ello, utilizaremos un nuevo tipo de layout, el RelativeLayout.
B. Implementación
B.1. Desarrollo de la primera interfaz
Vamos a crear un nuevo proyecto, en el que simplemente queremos hacer una actividad donde podamos escribir, por ejemplo para un bloc de notas o para una aplicación de correo. En este ejercicio únicamente trabajaremos con los layouts, por lo que vamos al
main.xml y bajo el TextView que se nos crea automáticamente, añadiremos dos nuevas vistas, un EditText y un botón. Como de lo que se trata es de escribir, queremos que el EditText ocupe casi toda la pantalla, por lo que fijaremos su altura mínima y máxima para
que ocupe casi todo y que nos quede el espacio justo para el botón. Así quedaría el XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
<TextView
android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hello">
</TextView>
<EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="fill_horizontal" android:minHeight="360dp" android:maxHeight="360dp">
</EditText>

<Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Just a Button">
</Button>
</LinearLayout>

El LinearLayout es de tipo vertical, luego todos sus hijos se colocarán uno debajo del otro. Es necesario decirle al EditText que su gravedad (cómo debe colocar los objetos que contiene, en este caso el texto) debe llenarlo horizontalmente, para que al escribir el EditText se vaya llenando línea a línea. También hemos fijado las alturas mínima y máxima
del EditText para forzarlo a que llene la pantalla de un dispositivo de resolución media, y
éste sería el resultado


Perfecto. ¿Qué ocurre si nuestros usuarios cambian sus HTC Magic, Vodafone Smart, Motorola Flipout, etc. por un Nexus One, HTC Desire, Samsung Galaxy S? Verían una interfaz similar a:


No podemos permitirnos que suceda esto. Además, debemos protegernos ante
circunstancias similares, dado que a lo mejor mañana introducen una resolución aún mayor, y si creamos las interfaces contando los píxels, tendremos problemas y los usuarios de nuestras aplicaciones no estarán contentos. Por supuesto, esto no sólo sucede con resoluciones más grandes, también sucede con dispositivos con resolución inferior para la que hemos
programado, como el HTC Wildfire:


El botón ha desaparecido. ¿Cómo solucionamos este problema?
B.2. La teoría de la relatividad (en Android)
Para solucionar esto, cambiaremos los LinearLayout, por los RelativeLayout.
En un RelativeLayout, le decimos al layout que queremos colocar una vista en una determinada posición dependiendo de otra vista, por ejemplo: “éste EditText a la derecha
de aquel TextView”. Esto nos permite, por ejemplo, decirle al RelativeLayout que nuestro EditText debe llenar espacio, y no ocupar el mínimo espacio necesario como antes, colocándose bajo el TextView y sobre el botón. Así queda nuestro segundo layout, el
adaptive_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content">
<TextView
android:id="@+id/layoutTop" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hello" android:layout_alignParentTop="true">
</TextView>
<Button android:id="@+id/layoutBottom" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Just a Button" android:layout_alignParentBottom="true">
</Button>

<EditText android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="fill_horizontal" android:layout_below="@id/layoutTop" android:layout_above="@id/layoutBottom">
</EditText>

</RelativeLayout>

Vayamos por partes. El RelativeLayout es, sin duda, el layout más potente que ofrece Android, pero también tiene muchos fallos, llegando incluso a tener un comportamiento un tanto extraño. Utilizarlo requiere que las vistas tengan un id asociado,
para que podamos referenciarlas y que el layout sepa a qué vista nos referimos. Es importante
tener en cuenta que, dependiendo de la versión de Android (las anteriores a 1.6 o Donut), no podemos hacer referencia a vistas que no hayamos declarado, es decir, el parser es de 1 sola pasada, por lo que a veces tenemos que recurrir a artimañas para que el layout sepa dónde
queremos colocar las vistas.
Empecemos. Tanto el TextView como el Button son casi idénticos, salvo porque tienen ids asociados y una nueva propiedad cada uno. En el caso del TextView, tenemos que android:layout_alignParentTop="true", esto significa que le decimos a Android
que esta vista debe ir pegada al límite superior del layout, luego lo que estamos haciendo es colocar el TextView arriba del todo. Con el Button decimos lo contrario, le decimos a
Android que layout_alignParentBottom="true", es decir, que esta vista debe ir justo
encima del límite inferior del RelativeLayout. Ya tenemos el TextView y el Button
colocados, independientemente del tamaño de la pantalla y de la resolución, porque usamos
los bordes de la pantalla como referencia. Pero aún no hemos hecho magia.
Para el EditText, le quitamos los atributos minHeight y maxHeight, y declaramos que debe ocupar toda la altura disponible o "match_parent". Luego, le decimos al relative que el EditText va bajo el TextView con la línea android:layout_below="@id/layoutTop" y que va sobre el Button con la línea android:layout_above="@id/layoutBottom". Con estas dos líneas tan sencillas, hemos
hecho que el layout se adapte automáticamente al tamaño que queda entre el TextView y el
Button. Veámoslo en el HTC Wildfire:


Y ahora en un dispositivo de alta resolución:


C. Conclusión
El RelativeLayout es una de las herramientas más potentes de las que nos provee
Android. Es flexible, cómodo y una vez entendemos cómo funciona, no querremos usar otro layout excepto para casos muy específicos. Sin embargo, cuesta entender su funcionamiento, y aconsejamos a los alumnos practicar con él:

--- Intentar tener dos botones juntos (uno al lado del otro a la misma altura) ocupando
cada uno la mitad de la anchura disponible. Pista: Colocar un TextView vacío en el
centro.
--- Proponer otra forma de representar las filas en el Ejercicio Avanzado de Bases de
Datos utilizando vistas personalizadas.