El diseño de la interfaz de usuario cobra cada día más importancia en el desarrollo de una aplicación. La calidad de la interfaz de usuario puede ser uno de los factores que conduzca al éxito o al fracaso de todo el proyecto.
Si has realizado alguna aplicación utilizando otras plataformas, advertirás que el diseño de la interfaz de usuario en Android sigue una filosofía muy diferente. En Android la interfaz de usuario no se diseña en código, si no utilizando un lenguaje de marcado en XML similar al HTML.
A lo largo de este capítulo mostraremos una serie de ejemplos que te permitirán entender el diseño de la interfaz de usuario en Android. Aunque no será la forma habitual de trabajar, comenzaremos creando la interfaz de usuario mediante código. De esta forma comprobaremos que cada uno de los elementos de la interfaz de usuario (las vistas) realmente son objetos Java. Continuaremos mostrando cómo se define la interfaz de usuario utilizando código XML. Pasaremos luego a ver las herramientas de diseño integradas en Android Studio. Se describirá el uso de layouts, que nos permitirá una correcta organización de las vistas, y el uso de recursos alternativos nos permitirá adaptar nuestra interfaz a diferentes circunstancias y tipos de dispositivos.
En este capítulo también comenzaremos creando la aplicación de ejemplo desarrollada a lo largo del curso, Asteroides. Crearemos la actividad principal, donde simplemente mostraremos cuatro botones, con los que se podrán arrancar diferentes actividades A continuación aprenderemos a crear estilos y temas y los aplicaremos a estas actividades. Para terminar el capítulo propondremos varias prácticas para aprender a utilizar diferentes tipos de vistas y layouts.
Objetivos:
• Entender cómo se realiza el diseño de la interfaz de usuario en una aplicación Android.
• Aprender a trabajar con vistas y mostrar sus atributos más importantes.
• Enumerar los tipos de layouts que nos permitirán organizar las vistas.
• Mostrar cómo se utilizan los recursos alternativos.
• Aprender a crear estilos y temas para personalizar nuestras aplicaciones.
• Mostrar cómo interactuar con las vistas desde el código Java o Kotlin.
• Describir el uso de layouts basados en pestañas (tabs).
Veamos un primer ejemplo de cómo crear una interfaz de usuario utilizando exclusivamente código Java. Aunque esta no es la forma recomendable de trabajar con Android, resulta interesante para resaltar algunos conceptos.
Ejercicio: Creación de la interfaz de usuario por código
1. Abre el proyecto creado en el capítulo anterior y visualiza MainActivity.java.
2. Comenta la última sentencia del método onCreate() y añade las subrayadas:
Nota sobre Java/Kotlin: Para poder utilizar el objeto TextView has de importar un nuevo paquete. Para ello añade al principio import android.widget.TextView;. Otra alternativa es pulsar Alt-Intro para que se añadan automáticamente los paquetes que faltan.
La interfaz de usuario de Android está basada en una jerarquía de clases descendientes de la clase View (vista). Una vista es un objeto que se puede dibujar y se utiliza como un elemento en el diseño de la interfaz de usuario (un botón, una imagen, una etiqueta de texto como la que se ha utilizado en el ejemplo, etc.). Cada uno de estos elementos se define como una subclase de la clase View; la subclase para representar un texto es TextView.
El ejemplo comienza creando un objeto de la clase TextView. El constructor de la clase acepta como parámetro una instancia de la clase Context (contexto). Un contexto es un manejador del sistema que proporciona servicios como la resolución de recursos, la obtención de acceso a bases de datos o las preferencias. La clase Activity es una subclase de Context, y como la clase MainActivity es una subclase de Activity, también es de tipo Context. Por ello, puedes pasar this (el objeto actual de la clase MainActivity) como contexto del TextView.
3. Después se define el texto que se visualizará en el TextView mediante setText(). Finalmente, mediante setContentView() se indica la vista que visualizará la actividad.
4. Ejecuta el proyecto para verificar que funciona.
En el ejemplo anterior hemos creado la interfaz de usuario directamente en el código. A veces puede ser muy complicado programar interfaces de usuario, ya que pequeños cambios en el diseño pueden corresponder a complicadas modificaciones en el código. Un principio importante en el diseño de software es que conviene separar todo lo posible el diseño, los datos y la lógica de la aplicación.
Android proporciona una alternativa para el diseño de interfaces de usuario: los ficheros de diseño basados en XML. Veamos uno de estos ficheros. Para ello accede al fichero res/layout/activity_main.xml de nuestro proyecto. Se muestra a continuación. Este layout o fichero de diseño proporciona un resultado similar al del ejemplo de diseño por código anterior:
NOTA: Cuando haces doble clic en el explorador del proyecto sobre activity_main.xml, probablemente lo abra en modo gráfico. Para verlo en modo texto, selecciona la pestaña Text.
Resulta sencillo interpretar su significado. Se introduce un elemento de tipo CoinstraintLayout, cuya función, como se estudiará más adelante, es contener otros elementos de tipo View. Este CoinstraintLayout tiene seis atributos. Los tres primeros, xmlns:android, xmlns:app y xmlns:tools, son declaraciones de espacios de nombres de XML que utilizaremos en este fichero (este tipo de parámetro solo es necesario especificarlo en el primer elemento). Los dos siguientes permiten definir la anchura y altura de la vista. En el ejemplo se ocupará todo el espacio disponible. El último atributo indica la actividad asociada a este layout.
Dentro del CoinstraintLayout solo tenemos un elemento de tipo TextView. Este dispone de varios atributos. Los dos primeros definen el alto y el ancho (se ajustarán al texto contenido). El siguiente indica el texto a mostrar. Los cuatro siguientes indican la posición de la vista dentro del CoinstraintLayout.
Ejercicio: Creación de la interfaz de usuario con XML
1. Para utilizar el diseño en XML regresa al fichero MainActivity.java y deshaz los cambios que hicimos antes (elimina las tres últimas líneas y quita el comentario).
2. Ejecuta la aplicación y verifica el resultado. Ha de ser muy similar al anterior.
3. Modifica el valor de hello_world en el fichero res/values/strings.xml.
4. Vuelve a ejecutar la aplicación y visualiza el resultado.
Analicemos ahora la línea en la que acabas de quitar el comentario:
setContentView(R.layout.activity_main)
Aquí, R.layout.main corresponde a un objeto View que se creará en tiempo de ejecución a partir del recurso activity_main.xml. Trabajar de esta forma, en comparación con el diseño basado en código, no quita velocidad y requiere menos memoria. Este identificador es creado automáticamente en la clase R del proyecto a partir de los elementos de la carpeta res. La definición de la clase R puede ser similar a:
public final class R {
public static final class attr {
}
public static final class drawable {
public static final int ic_launcher=0x7f020000;
}
public static final class id {
public static final int action_settings=0x7f070000;
}
public static final class layout {
public static final int activity_main=0x7f030000;
}
public static final class menu {
public static final int main=0x7f060000;
}
public static final class string {
public static final int app_name=0x7f040000;
...
NOTA: Este fichero se genera automáticamente. Nunca debes editarlo.
Has de tener claro que los identificadores de la clase R son meros números que informan al gestor de recursos sobre qué datos ha de cargar. Por lo tanto, no se trata de verdaderos objetos; estos se crearán en tiempo de ejecución solo cuando sea necesario usarlos.
Ejercicio: El fichero R.java
1. En Android Studio, el fichero R.java no es accesible desde el explorador del proyecto. No obstante, puedes acceder a él si pulsas con el botón derecho sobre app y seleccionas Show in Explorer (o Show in Dolfin). Desde esta carpeta abre el fichero:
app\build\generated\not_namespaced_r_class_sources\debug\r\nombre\del\paquete\R.java
Donde nombre\del\paquete has de reemplazarlo por el que corresponda al paquete de tu aplicación.
2. Compáralo con el fichero mostrado previamente. ¿Qué diferencias encuentras? (RESPUESTA: cambian los valores numéricos en hexadecimal y contiene muchos más identificadores.)
3. Abre el fichero MainActivity.java y reemplaza R.layout.activity_main por el valor numérico al que corresponde en R.java.
4. Ejecuta de nuevo el proyecto. ¿Funciona? ¿Crees que sería adecuado dejar este valor numérico?
5. Aunque haya funcionado, este valor puede cambiar en un futuro. Por lo tanto, para evitar problemas futuros vuelve a reemplazarlo por R.layout.activity_main.
Preguntas de repaso: Interfaz de usuario en XML y en código
Preguntas de repaso: El fichero R.java
Veamos ahora como editar los layouts o ficheros de diseño en XML. En el explorador del proyecto abre el fichero res/layout/activity_main.xml. Verás que en la parte inferior de la ventana central aparecen dos lengüetas: Design y Text. Podrás usar dos tipos de diseño: editar directamente el código XML (lengüeta Text) o realizar este diseño de forma visual (lengüeta Design). Veamos cómo se realizaría el diseño visual. La herramienta de edición de layouts se muestra a continuación:
NOTA: Si aparece un error con problemas de renderizado prueba otros niveles de API, en el desplegable que aparece junto al pequeño robot verde, o con otro tema, en el botón con forma de círculo.
En la parte inferior izquierda encontramos el marco Component Tree con una lista con todos los elementos del layout. Este layout tiene solo dos vistas: un ConstraintLayout que contiene un TextView. En el marco central aparece una representación de cómo se verá el resultado y a su derecha, con fondo azul, una representación con los nombres de cada vista y su tamaño. En la parte superior aparecen varios controles para representar este layout en diferentes configuraciones. Cuando diseñamos una vista en Android, hay que tener en cuenta que desconocemos el dispositivo final donde se visualizará y la configuración específica elegida por el usuario. Por esta razón, resulta importante que verifiques que el layout se ve de forma adecuada en cualquier configuración.
En la parte superior, de izquierda a derecha, encontramos los siguientes botones: El primero permite mostrar solo la visualización de diseño, solo la visualización esquemática de vistas o ambas. El botón
muestra la orientación horizontal (landscape), vertical (portrait) y también podemos escoger el tipo de interfaz de usuario (coche, TV, reloj…), con
escogemos el tipo de dispositivo (tamaño y resolución de la pantalla), con
la versión de Android, con
cómo se verá nuestra vista tras aplicar un tema y con
editar las traducciones.
Para editar un elemento, selecciónalo en el marco Component Tree o pincha directamente sobre él en la ventana de previsualización. Al seleccionarlo, puedes modificar alguna de sus propiedades en el marco Properties, situado a la derecha. Echa un vistazo a las propiedades disponibles para TextView y modifica alguna de ellas. En muchos casos te aparecerá un desplegable con las opciones disponibles. Aquí solo se muestra una pequeña parte de las propiedades disponibles. Pulsa en View all properties para mostrarlas todas.
El marco de la izquierda, Palette, te permite insertar de forma rápida nuevas vistas al layout. Puedes arrastrar cualquier elemento a la ventana de previsualización o al marco Component Tree. En el anexo D se ha incluido una lista con las vistas disponibles.
NOTA: El siguiente vídeo corresponde a una versión anterior de la herramienta. Aunque cambian algunos iconos el funcionamiento continúa siendo similar. Para crear un nuevo layout pulsa con el botón derecho en el explorador de proyecto sobre app y selecciona la opción: New > Android resource file
Vídeo[tutorial]: Diseño visual de layouts: visión general
Ejercicio: Creación visual de vistas
1. Crea un nuevo proyecto con los siguientes datos:
Phone and Tablet / Empty Activity
Name: Primeras Vistas
Minimum API level: API 19 Android 4.4 (KitKat)
Use androidx.* artifacts
Deja el resto de los parámetros con los valores por defecto.
6. Abre el fichero res/layout/activity_main.xml.
7. Vamos a hacer que la raíz del layout se base en un LinearLayout vertical. Este tipo de layout es uno de los más sencillos de utilizar. Te permite representar las vistas una debajo de la otra. En el marco Component Tree pulsa con el botón derecho sobre ConstraintLayout y selecciona Covert View… Indica que quieres usar el layout, LinearLayout. El layout que ha añadido es de tipo horizontal. En nuestro caso lo queremos de tipo vertical, para cambiarlo pulsa con el botón derecho sobre LinearLayout. y selecciona LinearLayout/Convert orientation to vertical.
Todas las operaciones que hacemos en modo diseño visual (lengüeta Design) también las podemos hacer con el editor de texto. Para probarlo, deshaz el trabajo anterior, pulsando el botón Undo (Ctrl+Z). Selecciona la lengüeta Text y cambia la etiqueta ConstraintLayout por LinearLayout. Añade el atributo orientation a LinearLayout para que la orientación sea vertical. Elimina los atributos innecesarios del TextView. que utiliza con ConstraintLayout. El resultado ha de ser similar a:
Regresa a la lengüeta Design.
8. Desde la paleta de la izquierda arrastra, al área de diseño, los siguientes elementos: ToggleButton, CheckBox, SeekBar y RatingBar.
9. Selecciona la primera vista que estaba ya creada (TextView) y pulsa <Supr> para eliminarla.
10. Selecciona la vista ToggleButton. Pulsa ahora (Set layout width to wrap_content). Conseguirás que la anchura del botón se ajuste a la anchura de su texto. Pulsa el botón
(Set layout width to match_parent) para que el ancho del botón se ajuste a su contenedor. Observa en el marco Attribues cómo cambia la propiedad layout_width. Si el botón anterior no funciona cámbialo desde este marco. Deja el valor wrap_content.
11. Pulsa el botón (Convert orientation to horizontal), para conseguir que el LinearLayout donde están las diferentes vistas tenga una orientación horizontal. Comprobarás que no caben todos los elementos. Pulsa el botón
(Convert orientation to vertical), para volver a una orientación vertical.
12. Con la vista ToggleButton seleccionada, pulsa el botón (Set layoutheight to match_parent). Conseguirás que la altura del botón se ajuste a la altura de su contenedor. El problema es que el resto de los elementos dejan de verse. Vuelve a pulsar este botón para regresar a la configuración anterior (también puedes pulsar Ctrl-Z).
13. Selecciona la vista CheckBox. Ve al marco Attributes y en la parte inferior pulsa en All attributes. Busca la propiedad layout_margin en el campo all introduce “20dp”. Se añadirá un margen alrededor de la vista.
14. Busca la propiedad gravity y selecciona center.
15. Observa que hay un espacio sin usar en la parte inferior del layout. Vamos a distribuir este espacio entre las vistas. Desde el marco Commponet Tree selecciona las cuatro vistas que has introducido dentro del LinearLayout. Para una selección múltiple mantén pulsada la tecla Ctrl.
16. Aparecerá un nuevo botón: (Distribute Weights Evenly). Púlsalo y la altura de las vistas se ajustará para que ocupen la totalidad del layout. Realmente, lo que hace es dividir el espacio sin usar de forma proporcional entre las vistas. Es equivalente a poner layout_weight = 1 y layout_height = 0dp para todas las vistas de este layout. Esta propiedad se modificará en un siguiente punto.
17. Selecciona las cuatro vistas y pulsa el botón (Clear All Weight) para eliminar los pesos introducidos.
18. Selecciona la vista CheckBox. Asigna, en el marco Attributes, layout_height = 1 y layout_weight = 0dp en esta vista. Observa como toda la altura restante es asignada a la vista seleccionada.
19. Para asignar un peso diferente a cada vista, repite los pasos anteriores donde asignábamos peso 1 a todas las vistas (botón: ). Pulsa la lengüeta Text y modifica manualmente el atributo layout_weight para que el ToggleButton tenga valor 2; CheckBox tenga valor 0.5; SeekBar valor 4 y RatingBar valor 1. Pulsa la lengüeta Design. Como puedes observar, estos pesos permiten repartir la altura sobrante entre las vistas.
20. Utiliza los siguientes botones: para ajustar el zum.
21. Utiliza los botones de la barra superior para observar cómo se representará el layout en diferentes situaciones y tipos de dispositivos:
22. Selecciona la vista CheckBox y observa las diferentes propiedades que podemos definir en el marco Attributes. Algunas ya han sido definidas por medio de la barra de botones. En concreto, y siguiendo el mismo orden que en los botones, hemos modificado: Layout margin = 20dp, gravity = center y Layout weight = 0.5.
23. Busca la propiedad Text y sustituye el valor CheckBox por “Guardar automáticamente” y Text size por “9pt”.
24. Pulsa el botón para mostrar la visualización de diseño, la esquemática (Blueprnt) o ambas. A continuación, se muestra el resultado final obtenido:
25. Pulsa sobre la lengüeta Text. Pulsa las teclas Ctrl-Alt-L para que formatee adecuadamente el código XML. A continuación, se muestra este código:
26. Ejecuta el proyecto para ver el resultado en el dispositivo.
Ejercicio: Vistas de entrada de texto
1. Añade en la parte superior del layout anterior una vista de tipo entrada de texto EditText, de tipo normal (Plain Text). Lo encontrarás dentro del grupo Text. Debajo de esta, una de tipo correo electrónico (E-mail) seguida de una de tipo palabra secreta (Password). Continua así con otros tipos de entradas de texto.
2. Ejecuta la aplicación.
3. Observa como al introducir el texto de una entrada se muestra un tipo de teclado diferente.
Vídeo[tutorial]: Atributos de la clase View en Android
Vídeo[tutorial]: Atributos de la clase TextView en Android
Recursos adicionales: Atributo de dimensión
En muchas ocasiones tenemos que indicar la anchura o altura de una vista, un margen, el tamaño de un texto o unas coordenadas. Este tipo de atributos se conocen como atributos de dimensión. Dado que nuestra aplicación podrá ejecutarse en una gran variedad de dispositivos con resoluciones muy diversas, Android nos permite indicar estas dimensiones de varias formas. En la siguiente tabla se muestran las diferentes posibilidades:
px | (píxeles): Estas dimensiones representan los píxeles en la pantalla. |
mm | (milímetros): Distancia real medida sobre la pantalla. |
in | (pulgadas): Distancia real medida sobre la pantalla. |
pt | (puntos): Equivale a 1/72 pulgadas. |
dp | (píxeles independientes de la densidad): Presupone un dispositivo de 160 píxeles por pulgada. Si luego el dispositivo tiene otra densidad, se realizará el correspondiente ajuste. A diferencia de otras medidas como mm, in y pt este ajuste se hace de forma aproximada dado que no se utiliza la verdadera densidad gráfica, si no el grupo de densidad en que se ha clasificado el dispositivo (ldpi, mdpi, hdpi…). Esta medida presenta varias ventajas cuando se utilizan recursos gráficos en diferentes densidades. Por esta razón, Google insiste en que se utilice siempre esta medida. Desde un punto de vista práctico un dp equivale aproximadamente a 1/160 pulgadas. Y en dispositivos con densidad gráfica mdpi un dp es siempre un pixel. |
sp | (píxeles escalados): Similar a dp, pero también se escala en función del tamaño de fuente que el usuario ha escogido en las preferencias. Indicado cuando se trabaja con fuentes. |
Recursos adicionales: Tipos de vista y sus atributos
Consulta el anexo D para conocer una lista con todos los descendientes de la clase View y sus atributos.
Preguntas de repaso: Las vistas y sus atributos