A la hora de desarrollar una aplicación Android deberemos tomar varias decisiones muy importantes, como puede ser, el nivel de API mínimo que vamos a soportar y el tamaño de pantalla de los dispositivos para los que nuestra aplicación va a estar optimizada.
En el primero de los casos cuanto menor sea el nivel del API nos aseguraremos un mayor mercado potencial de usuarios-dispositivos que van a poder descargar nuestra aplicación. A cambio, perderemos la posibilidad de utilizar las nuevas características que se van añadiendo en cada API así como tener que utilizar clases y métodos deprecados en vez de los que les sustituyen y que están más optimizados.
Lo mismo sucede respecto al tipo de pantallas que la aplicación va a tener en cuenta. Una de las ventajas de Android es que cualquier fabricante puede diseñar un teléfono, con las características que desee y utilizar como sistema operativo Android. También, permite, que no sólo sea un sistema operativo móvil, ya que puede utilizarse en múltiples dispositivos, relojes, tabletas, televisiones, etc. Sin embargo, el desarrollador tendrá que pensar qué tipo de dispositivos y tamaños va a querer que puedan utilizar su aplicación. Para ello, vamos a ver qué mecanismos nos ofrece Android para poder utilizar nuestra aplicación en distintas pantallas.
Esta documentación es aplicable a partir de Android 1.6 (API de nivel 4).
A la hora de ver cómo podemos soportar varias pantallas, debemos tener en cuenta tanto el tamaño como la densidad. Entendemos por tamaño, como la medida de la diagonal de la pantalla. La densidad es el número de píxeles dentro de un área física de la pantalla, medidas en dpi (dots per inch).
Por suerte, no vamos a tener que trabajar cada una de las posibles configuraciones, ya que Android trabaja con rangos tanto de tamaños como de densidades. Es decir, que no vamos a tener que crearnos un layout para cada tamaño de pantalla o densidad. Por ejemplo, un dispositivo con una pantalla de 3,5 pulgadas y otro con una pantalla de 4 pulgadas, entrarían dentro del mismo rango, con lo cual sólo deberíamos de optimizar un layout para ambos dispositivos.
Los rangos de tamaño de pantalla que vamos a poder manejar son:
A partir de Android 3.2 (Api 13), estos grupos son deprecados, a favor de las nuevas técnicas de manejo de pantallas, que se basan en el ancho de la misma y que veremos más adelante.
Igualmente, tenemos cuatro grupos generales para la densidad:
Estos grupos están organizados alrededor de la configuración básica formada por el tamaño normal y la densidad mdpi que corresponden con el primer dispositivo Android, el T-Mobile G1.
La densidad, es un elemento muy importante, pues pantallas con el mismo tamaño, y distinta densidad, van a tener diferente espacio disponible. Android utiliza dp (densidad independiente del píxel) como unidad de medida virtual que no depende de la densidad. Es equivalente a un píxel en una pantalla con una densidad de 160 dpi, que estaría en el rango de la densidad media. Utilizando esta medida, Android, realizará todo el proceso de conversión de tamaños a píxeles, lo que nos asegura la compatibilidad con los distintos tipos de densidad. En las aplicaciones podemos expresar medidas en píxeles, pero esto puede causar una mala visualización en pantallas con diferentes densidades.
El tamaño mínimo que tendrán que tener nuestros layouts en dps son:
Cuando creemos la aplicación, podremos crear recursos específicos, para cada grupo de tamaño y para cada grupo de densidad. Para el tamaño tendremos que crearnos distintos layouts y para la densidad deberemos proveer bitmaps con resoluciones diferentes.
Una aplicación será “independiente de la densidad”, cuando conserve el tamaño físico (desde el punto de vista del usuario) de los elementos de la interfaz de usuario, cuando sea visualizado en pantallas con diferentes densidades.
Con el siguiente ejemplo, se comprende fácilmente, la importancia de conservar la densidad. Tenemos tres pantallas con distintas densidades y el mismo botón y la misma imagen en cada una (todos los tamaños los hemos expresado en píxeles). Sin embargo, en la pantalla con densidad más baja, el botón y la imagen se ven más grandes, mientras, que en la pantalla con la densidad mayor, estos elementos se ven más pequeños. Además, en la pantalla con densidad más baja, la imagen aparece píxelada.
Es lógico, si tenemos en cuenta, que la densidad es el número de píxeles que entran en un área física, en las pantallas con baja densidad, tendremos menos píxeles, con lo que una imagen de 32 × 32 píxeles ocupará más área física de pantalla. Y viceversa para pantallas con alta densidad.
En la siguiente imagen, vemos como se mostraría al usuario, los elementos anteriores, una vez que hemos asignado recursos para los distintos tipos de densidades. Ahora, las tres pantallas muestran los elementos de forma similar.
Para poder lograr que nuestros elementos se muestren idénticamente en cualquier densidad debemos expresar las medidas de los elementos en dps. Un botón con un tamaño de 64 dp x 64 dp tendrá el mismo tamaño (en dps) para cualquier densidad. El sistema será el encargado de transformar ese botón en los píxeles adecuados para que mantenga ese aspecto en cualquier pantalla.
Ya tenemos solucionado el problema del escalado de los elementos. Sin embargo, las imágenes, van a píxelarse. Para evitarlo, es necesario que creemos recursos alternativos para los bitmaps.
Si decidimos, que no queremos que nuestra aplicación sea soportada en todas las pantallas, deberemos indicar los dispositivos soportados en el fichero AndroidManifest.xml. De esta forma, un dispositivo que no esté soportado no podrá descargarse la aplicación. Para ello, tendremos que utilizar el elemento "<supports-screens>", indicando los grupos de tamaño o de densidad soportados.
Por defecto, trabajando con dps y el atributo “wrap_content”, Android redimensiona los layouts de nuestra aplicación, ajustándoles a la pantalla del dispositivo. Esto será suficiente para casi todos los casos. Sin embargo, puede, que en pantallas grandes, queramos aprovechar el mayor espacio para variar la distribución, poder separar más los elementos, ampliarles, etc, o en pantallas pequeñas tener que ajustar nuestro diseño a la disminución del espacio disponible. Para ello, es necesario crearnos un layout para cada grupo de tamaño que queramos que muestre un aspecto distinto. Estos layouts, hay que guardarles dentro de la carpeta resources. Para cada tamaño, crearemos una carpeta del tipo "layout-xlarge/", "layout-small/", etc.
Igualmente, para los bitmaps dibujables, Android, puede redimensionar los ficheros .png, .jpg, .gif y nine-patch(.9.png) intentando renderizarlos de forma correcta al tamaño físico de cada dispositivo. Si queremos asegurarnos, que se renderizan correctamente, tendremos que crearnos estos ficheros para cada grupo de densidades y almacenarlos en la carpeta "resources" en la carpeta correspondiente. Estas tendrán el formato "drawable-grupo_de_densidad", por ejemplo, "drawable-hdpi".
Un fichero nine-patch es básicamente un fichero png en el que se especifican dos regiones que son estirables. Si el sistema tiene que escalar la vista estira el nine-patch, pero sólo va a estirar las dos regiones especificadas. De esta forma, el mismo recurso, funcionará para distintos tipos de pantallas.
A la hora de crear los layouts, no hay que olvidarnos, que la pantalla puede mostrarse en orientación vertical u horizontal, con lo que el tamaño va a cambiar radicalmente. Es posible crearnos layouts específicos para cada orientación, guardándoles con el calificador "land" o "port".
Cuando creemos los nuevos bitmaps debemos respetar el ratio de escalado 3:4:6:8. Por ejemplo, para un bitmat de 48×48 píxeles que se utilice en una pantalla de densidad media deberíamos crear uno de 36×36 para densidades bajas (x0.75), 48×48 para densidades medias, 72×72 para densidades altas(x1,5) y finalmente 96×96 (x2) para las pantallas con densidades altas.
Todo lo referente a los cuatro grupos de tamaños de pantallas es sólo vigente hasta Android 3.2. A partir de esta versión, se ha creado un nuevo sistema de gestión de los tamaños de pantalla, basándose en el ancho o alto de las mismas, siempre expresado en dp.
Antes de definir el tamaño que vamos a necesitar para nuestro layout, debemos tener en cuenta, que Android puede usar elementos de la interfaz de usuario (como la barra de sistema en la parte inferior de la pantalla o la barra de estatus en la parte superior) . Así que no tendremos disponible todo el espacio de la pantalla para nuestro layout.
Tenemos disponibles los siguientes nuevos calificadores de recursos para crear nuestras carpetas contenedoras de los layouts:
Si nos creamos un layout dentro del recurso “res/layout-sw600dp”, este se usará por el sistema, siempre y cuando la pantalla, da igual en la orientación en que esté, alcance los 600 dp.
Veamos unos ejemplos de tamaños mínimos en dp para distintos dispositivos:
Si quisiésemos crear una aplicación para un teléfono y una tablet (y el menor tamaño que van a poder tener las tablets, para poder usar el layout personalizado es de 600dp) tendríamos dos recursos:
Si necesitamos, especificar un layout diferente, para pantallas de 600dp y de 720dp, tendríamos un recurso más:
En estos casos, no importa si el ancho viene dado por la orientación horizontal o vertical. Si queremos, utilizar un layout sólo cuando el ancho sea superior a 600 dp tendremos que crearnos el recurso:
Si cambiamos la orientación, y el tamaño del ancho es menor de 600, se utilizará el layout del recurso: res/layout/main_activity.xml.
Al igual que ocurría con los primeros grupos genéricos de tamaño, debemos declarar en el AndroidManifest.xml qué tamaños va a soportar nuestra actividad. Utilizaremos el mismo elemento "supports-screens" con el nuevo atributo "android:requiresSamallestWidthDp", con el que indicamos el ancho mínimo necesario para poder utilizar la aplicación.