Parte 1 – Recursos
Introducción
Durante el Google IO 2010, una de las charlas que más me interesó fue la impartida por Justin Mattson llamada “Casting a wide net: how to target all Android devices” que trató de como hacer aplicaciones, que puedan ser usadas exitosamente, en todos los dispositivos Android. Puede que suene un poco audaz, pero sí es posible. Tenemos que tener claro que no para todas las aplicaciones es posible, pero se pueden tomar algunos consejos para poder permitir a la aplicación ser ejecutada, al menos, en la mayor cantidad de dispositivos posible.
Lamentablemente la charla fue corta, para la importancia del contenido por lo que quiero hacer esta serie de entradas, para explicar un poco más a detalle lo explicado en la presentación, basado tanto en el contenido de la misma, como en mi experiencia. Para ello, les explicaré con un ejemplo llamado Blue Monkey Eyes, una aplicación que hice para poder entender bien estos conceptos. La aplicación es bastante absurda, ya que no hace más que hacer ruidos, pero lo que veremos es el cómo se hizo.
Para esta primera parte discutiremos lo siguiente:
- Densidad o DPI (de sus siglas en inglés Dots per Inch)
- Resolución de pantallas (alto por ancho)
- Orientación (horizontal y vertical)
Densidad de Pantallas
En pocas palabras, cuantos puntos por pulgada hay en una superficie (dpi). No hay que confundir pixeles por pulgada (ppi). La importancia de la densidad es que esto permite que al crear una imagen o texto tenga el mismo tamaño real, sin importar la resolución o tamaño físico de la pantalla. Veamos la siguiente imagen:
Es importante que a la hora de diseñar los recursos de la aplicación, se defina bien los tamaños que se soportarán para así determinar la densidad o densidades y hacer un conjunto de recursos por cada uno. La plataforma Android nos permite definir carpetas por densidad de pantalla usando “calificadores” o “sufijos”. Para la densidad, están definidos 4 calificadores para la carpeta drawable:”-hdpi”, “-mdpi”, “-ldpi” y “nodpi”. Simplemente creamos las carpetas “drawable-[hpdi|mdpi|ldpi]” según las densidades que se soportarán. Es importante que los recursos mantengan el nombre a travéz de los folders, para que así la plataforma pueda hacer la selección sin problemas.
Existe una “pulga” o “bug” en la version 1.5 (SDK 3), ya que esta estructura de carpetas fue introducida hasta la versión 1.6. El problema con 1.5 es que puede escoger inadecuadamente el recurso, aunque éste se encuentre en la carpeta “drawable”. Para resolver esto, se debe agregar otro sufijo (después de -[hdpi|mdpi|ldpi]) a las carpetas que serán utilizadas para soportar diferentes densidades en dispositivos mayores a la version 3 del SDK. El sufijo a utilizar es “-v[4…n]” donde n es el número de SDK mínimo que la aplicación soportará para multi densidad y se deja “drawable” para que el SDK 3 tome las imágenes. Creo que esta mejor explicado en http://developer.android.com/guide/practices/screens_support.html#qualifiers.
Resolución de Pantallas
Actualmente, diferentes compañías han adoptado Android para la fabricación de dispositivos móviles, algunos de ellos son HTC (que sacó el primer dispositivo Android), Samsumg, Motorola entre otros. Debido a que no existe una estandariación en cuanto al hardware que soporta Android, tenemos a disposición diferentes modelos para diferentes necesidades. La visión de Google en cuanto dejar abierto a la innovación queda muy claro a este punto. Tenemos los dispositivos tipo tablets a la vuelta de la esquina y no muy lejos Google TV que también se basará en android.
En la documentación oficial de Android se encuentra disponible una guía para poder soportar múltiples pantallas (http://developer.android.com/guide/practices/screens_support.html). Ahí definen “cajones” de como se dividen las pantallas y como se distribuye las densidades en ellas formando la siguiente matriz:
Baja densidad (120), ldpi | Densidad Media (160), mdpi | Alta densidad (240), hdpi | |
Pantalla Pequeña |
|
||
Pantalla Normal |
|
|
|
Pantalla Grande |
|
Android ofrece los calificadores “small”, “normal” y “large” para poder separar los recursos de acuerdo al tamaño de la pantalla.
Orientación Horizontal y Vertical
Tal vez una de las cualidades más discutidas en la comunidad Android, es la capacidad de cambiar el aspecto cuando se tiene el dispositivo de forma vertical o horizontal. Dentro de las preguntas más frecuentes que se puede leer en el grupo de usuarios de Android es como desactivar esta facilidad. Lo que recomienda Google es no pelear con él sino aprender a manejarlo.
Hay que tener bien claro que estoy no aplica para todas las aplicaciones. Por ejemplo, algunos juegos ocupan que a pesar de que se cambie la orientación del dispositivo, se mantenga todo como está, para evitar una experiencia de usuario pobre y por que el game play lo requiere así.
Si su aplicación requiere que se maneje tanto una orientación vertical (portrait) como horizontal (landscape), puede utilizar los calificadores “-port” y “-land” para la carpeta de layout.
La carpeta debiera verse así:
Dando como resultado que la aplicación se vea así en ambas posiciones:
Aqui el XML de ambos layouts.
Layout por defecto de Obey the Monkey
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res/com.fr4gus.android.blueeyesmonkey" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <com.admob.android.ads.AdView android:id="@+id/ad" android:layout_width="fill_parent" android:layout_height="wrap_content" app:backgroundColor="#000000" app:primaryTextColor="#FFFFFF" app:secondaryTextColor="#CCCCCC" app:refreshInterval="15" app:keywords="Android Silly Application" /> <ImageView android:id="@+id/ImageMonkey" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/monkey_inactive" android:baselineAlignBottom="true" android:layout_gravity="center_vertical|center_horizontal" /> <LinearLayout android:id="@+id/ButtonsLayout" android:layout_width="fill_parent" android:orientation="vertical" android:layout_height="wrap_content" android:layout_gravity="bottom|center_horizontal"> <Button android:id="@+id/ObeyButton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/obeyButtonLabel" android:gravity="center_horizontal|center_vertical" android:background="@drawable/button_states" android:textColor="#FFFFFF" /> <Button android:id="@+id/ListenButton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/listenButtonLabel" android:typeface="normal" android:textColor="#FFFFFF" android:background="@drawable/button_states" /> </LinearLayout> </LinearLayout>
Layout en modo landscape de Obey the Monkey
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res/com.fr4gus.android.blueeyesmonkey" android:orientation="vertical" android:layout_gravity="center_vertical|center_horizontal" android:layout_height="fill_parent" android:layout_width="fill_parent"> <com.admob.android.ads.AdView android:id="@+id/ad" android:layout_width="fill_parent" android:layout_height="wrap_content" app:backgroundColor="#000000" app:primaryTextColor="#FFFFFF" app:secondaryTextColor="#CCCCCC" app:refreshInterval="15" app:keywords="Android Silly Application" /> <LinearLayout android:id="@+id/contentLayout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_gravity="right|center_vertical"> <ImageView android:id="@+id/ImageMonkey" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/monkey_inactive" android:layout_gravity="center_horizontal|center_vertical"></ImageView> <LinearLayout android:id="@+id/ButtonsLayout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" android:layout_gravity="right|center_vertical"> <Button android:id="@+id/ObeyButton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/obeyButtonLabel" android:gravity="center_horizontal|center_vertical" android:textColor="#FFFFFF" android:layout_gravity="center_vertical|center_horizontal" android:background="@drawable/button_states"></Button> <Button android:id="@+id/ListenButton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/listenButtonLabel" android:textColor="#FFFFFF" android:typeface="normal" android:layout_gravity="center_vertical|center_horizontal" android:background="@drawable/button_states"></Button> </LinearLayout> </LinearLayout> </LinearLayout>
Resumen
La importancia de poder soportar diferentes pantallas, radica principalmente en el mercado meta de su aplicación. Entre más dispositivos soporte, a más usuarios podra llegar. Pero debe tomar en cuenta los requerimientos de la aplicación así como velar que la experiencia de usuario sea grata también.
Para la segunda parte revisaremos como manejar las diferentes versiones de SDKs y como podemos afrontar el problema de objetos o métodos disponibles en uno e innexistentes en otros.
Calificadores vistos:
Pantalla | Calificador o Sufijo | Descripción |
---|---|---|
Tamaño | small |
Recursos para pantallas pequeñas, como QVGA en baja densidad |
normal |
Recursos para pantallas normales, como las pantallas del T-Mobile G1/HTC Magic o equivalente | |
large |
Recursos para pantResources for large screens. Typical example is a tablet like device. | |
Densidad | ldpi |
Recursos de baja densidad (entre 100 y140 dpi). |
mdpi |
Recursos de mediana densidad (entre 140 y 180 dpi). | |
hdpi |
Recursos de alta densidad (entre 190 y 250 dpi) | |
nodpi |
Recursos independientes de densidad. La plataforma no intentara escalar automáticamente recursos bajo este calificador sin importar la densidad de la pantalla actual. | |
Relación de Aspecto | long |
Recursos para pantalla de cualquier tamaño y densidad, donde la relación de acpecto tanto en alto (modo portrait) o ancho (modo landscape) sea significativamente mayor que la configuración base de la pantalla. |
notlong |
Recursos para pantallas donde la relación de aspecto es similar a la configuración base de la pantalla. | |
Versión de Plataforma | v<api-level> |
Recursos que solo se usarán para una versión específica del API o mayor. Por ejemnplo, si su aplicación corre en Android 1.5 (API Level 3) y Android 1.5 (API Level 4 omayor) usted puede usar el calificador -v4 para excluir aquellos recursos cuando la aplicación va a correr Android 1.5 (API Level 3). |
Vertical | port |
Recursos que se utilizarán para la pantalla en modo portrait |
Horizontal | land | Recursos que se utilizarán para la pantalla en modo landscape |
Links importantes
- [Inglés]Proveyendo Recursos: http://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources
- [Inglés]Mejores Practicas – Soportando múltiples pantallas: http://developer.android.com/guide/practices/screens_support.html
- [Inglés]Justin Mattson – “Casting a wide net: how to target all Android devices” https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BfZ2urbZk7o
Amigo fr4gus me gustaría saber cual es tu opinión sobre el fallo de la Librería del Congreso de los Estados Unidos sobre la legalidad del jailbreaking en teléfonos celulares y como esto podría el desarrollo de aplicaciones para dispositivos móviles (especialmente en el iphone). Te dejo un link:
http://www.neoteo.com/fallo-historico-en-contra-del-drm.neo
Saludos
Por aqui me encontre el texto original: http://www.copyright.gov/1201/
Me parece super importante hacer notar que todo es basado en soluciones sin fin lucrativo. Si usted tiene el software para poder hacerle jailbreak a SU iPhone o rootear su telefono Android no hay problema. Habra que ver como se interpreta para aquella gente que cobra por hacer estas cosas.