La columna 80

El blog técnico-personal de Ángel J. Vico… en español

Diseñando interfaces en Android: XML y propiedades

Posted by Ángel J. Vico en 17 de septiembre de 2011

En posts anteriores vimos de qué elementos disponemos en Android para diseñar las interfaces de usuario de nuestras aplicaciones. Por un lado tenemos actividades para crear cada una de las diferentes pantallas, y vistas para crear el contenido de cada actividad. Además, también hemos visto que tenemos dos tipos de vistas: diseños para subdividir la actividad y situar el resto de vistas, y widgets para mostrar información al usuario y permitirle interactuar con la aplicación.

En este post vamos a ver cómo se crean todos estos elementos y qué propiedades tienen en común. Más adelante veremos cómo, una vez creados, los incluimos y mostramos en nuestras actividades.

Podemos crear widgets y diseños tanto por código como utilizando archivos de recursos en XML situados en la carpeta layouts. Ni qué decir tiene que es preferible optar por la segunda opción, que no sólo permite tener una vista previa aproximada de la interfaz en tiempo de codificación, sino también disponer de diferentes interfaces de usuario para una misma actividad. Esto permite escoger la más adecuada en tiempo de ejecución en función del tamaño de la pantalla o del idioma que tenga configurado el dispositivo, por ejemplo.

Aunque me centraré en los recursos XML, también iré mencionando los elementos Java que se corresponden con ellos. En este otro post explico cómo se crean interfaces de usuario completas por código.

Diseño en XML

Para empezar, veamos un pequeño ejemplo de interfaz de usuario creada en XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/principal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <TextView
        android:id="@+id/etiqueta1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Esto es una etiqueta de texto">
    </TextView>
    <Button
        android:id="@+id/boton1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Esto es un botón">
    </Button>
</LinearLayout>

Se trata de un diseño LinearLayout vertical que ocupa todo el área disponible de la actividad y que contiene dos widgets: una etiqueta de texto y un botón. Este diseño genera la interfaz de usuario que se muestra en las siguientes capturas. Hay que tener en cuenta que el aspecto de los widgets depende la versión de Android que utilicemos, por lo que variará de un dispositivo a otro o de un emulador a otro. La captura de la izquierda está hecha con Android 1.5 y la de la derecha con la versión 2.3:

Interfaz de usuario en Android 1.5Interfaz de usuario en Android 2.3

La estructura del archivo XML es de tipo árbol, con un elemento principal que hace de raíz y una serie de elementos que se incluyen unos dentro de otros a diferentes niveles de profundidad. Lo más importante aquí es que sólo tenemos un elemento raíz del que dependerán, directa o indirectamente, el resto de vistas. Lo habitual es que ese elemento sea un diseño que define la organización principal de la actividad y que contiene el resto de diseños y widgets. No obstante, también podría ser un widget, si es lo único que necesita la actividad.

En el ejemplo, la raíz es un LinearLayout vertical, definido con la etiqueta XML <LinearLayout>. Por comodidad, la mayoría de etiquetas y atributos de los archivos XML se llaman de forma idéntica a las clases y propiedades con que se corresponden (aunque hay excepciones). Dentro de la etiqueta <LinearLayout> se incluyen otras dos, <TextView> y <Button>, que se corresponden con los widgets del mismo nombre (la etiqueta de texto y el botón que se muestran en las capturas).

Una pequeña aclaración

En Android se utiliza el término diseño (layout) para referirse tanto a la vista que contiene otras vistas y se encarga de organizarlas y dimensionarlas, como al conjunto de vistas (incluyendo widgets y diseños) que se muestran en una actividad. Por este motivo la carpeta en la que se incluyen los archivos XML como el de arriba se llama también layout.
En este blog utilizaré el término diseño para referirme a ambas cosas indistintamente. Espero que el contexto deje claro de qué estoy hablando en cada momento.

Propiedades de las vistas

El aspecto y el comportamiento de las vistas, tanto diseños como widgets, se pueden personalizar mediante propiedades. En el archivo XML las propiedades se representan mediante atributos de las etiquetas que se corresponden con las vistas. En la API, las propiedades son atributos privados o protegidos de las clases que implementan las vistas.

Para modificar una propiedad sólo tenemos que darle un valor al atributo correspondiente, incluyéndolo dentro de la etiqueta XML que representa la vista. Sin embargo, por código no podemos hacer lo mismo, dado que los atributos privados o protegidos no son accesibles y el lenguaje java no permite definir propiedades en las clases como hacen otros lenguajes (C#, por ejemplo). Por este motivo, cada atributo XML se suele corresponder con un método de la clase cuyo nombre coincide (casi siempre) con el del atributo, pero con la cadena set como prefijo. Este prefijo es una convención habitual para indicar que el método sirve para establecer el valor de un atributo de la clase. Si la clase permite también recuperar el valor del atributo, existirá un método similar con prefijo get.

Hay que tener en cuenta, que cuando hablo del nombre del atributo me refiero a lo que va a continuación de android:. Este prefijo que llevan todos los atributos del archivo XML es el espacio de nombres en el que se definen. Sin entrar en muchos detalles (no es el objetivo de este post) los espacios de nombres en XML tienen una función similar a la de los paquetes en Java (salvando las distancias): permiten definir símbolos dentro de un ámbito de forma que no tengan conflictos, por tener el mismo nombre, con los de otro ámbito. Los espacios de nombres se definen en esquemas XML que incluyen las etiquetas disponibles, los atributos que puede tener cada etiqueta y qué etiquetas pueden estar contenidas dentro de otras. En nuestro archivo XML, anteponer el prefijo android: sirve para especificar el espacio de nombres donde se declara el atributo. El espacio de nombres está declarado en el atributo xmlns de la etiqueta <LinearLayout> y aunque pueda parecer una URL (dirección web) en realidad sólo es un identificador con forma de URL.

La API de Android aprovecha la herencia de java para homogeneizar las propiedades disponibles para las vistas. Dado que las propiedades son atributos de clases, pueden ser heredadas por las clases hijas (si se definen como protegidas y no como privadas). De esta forma, como todas las vistas heredan directa o indirectamente de la clase View, las propiedades que se definen en esta clase estarán disponibles en todas las hijas.

Identificadores

Una de las propiedades que proporciona la clase View es la que se utiliza en el ejemplo como android:id. Se trata de un identificador para las vistas, aunque no es obligatorio. En el archivo XML, asignar un identificador a una vista sirve para poder hacer referencia a ella tanto en el propio archivo XML como en la actividad que lo utilice. Si no vamos a referenciar una vista no es necesario asignarle un identificador.

Para poder usar el identificador XML desde el código Java, el compilador genera una clase llamada R en la que se incluye una constante por cada atributo Id del archivo XML. El método findViewById de la clase Activity nos permite obtener la vista que necesitamos a partir de estas constantes de la clase R.

Por otro lado, cuando la interfaz de usuario se crea por entero desde el código tenemos objetos definidos para cada vista y podemos usarlos para acceder a sus propiedades o como referencia en otras vistas, sin necesidad de asignar identificadores.

Aunque a primera vista no lo parece, el identificador es en realidad un número. De hecho, el método setId que permite establecer el identificador por código recibe un entero (int) como parámetro. Sin embargo, trabajar directamente con identificadores numéricos en los archivos XML no resulta demasiado práctico, por lo que el compilador nos permite utilizar unas cadenas de texto especiales que luego son traducidas a los números que correspondan. El formato de esas cadenas de texto es el siguiente:

   @[<nombre_de_paquete>:|+]<tipo_de_recurso>/<nombre_de_recurso>
  • El símbolo @ del comienzo le indica al procesador del archivo XML que va a ser necesario expandir la cadena que viene a continuación reemplazándola por el valor correspondiente.

  • El siguiente campo, opcional, puede ser un nombre de paquete o el símbolo +. El símbolo + indica que el recurso al que se hace referencia no existe y tiene que crearlo el compilador. Sólo se utiliza para identificadores. De esta forma, el compilador asigna un valor numérico único a cada identificador. El nombre de paquete se utiliza cuando se quiere hacer referencia a un recurso que ha sido creado en otra parte.

  • Los dos siguientes campos son obligatorios: el tipo de recurso y su nombre. En nuestro caso el tipo de recurso es id, pero este formato también sirve para hacer referencia a otros recursos, cada uno de los cuales tiene un tipo concreto. Por su parte, el nombre del recurso es el identificador que vamos a usar para la vista dentro del archivo XML. Por lo tanto, no deberíamos asignar el mismo nombre a diferentes vistas.

Nota

Aunque el compilador no va a protestar si usamos el mismo nombre en más de una vista, sólo se va a crear un identificador y, por tanto, una única constante en la clase R. Si tratamos de localizar la vista que le corresponde, se devolverá siempre la primera que se encuentre con el identificador buscado, lo que puede dar lugar a comportamientos no deseados. Incluso, si el tipo de la vista que se devuelve no se corresponde con el del objeto al que se intenta asignar, se producirá un error en la aplicación durante su ejecución.

En resumen, si asignamos a un atributo Id una cadena como @+id/principal, el compilador le asignará un número a la vista, por ejemplo el 0x7f050000 (2.131.034.112 en hexadecimal), y creará una constante de nombre R.layout.principal en el archivo R.java. La constante será de tipo int y tendrá el valor 0x7f050000. Además, cuando se cree el objeto correspondiente a la vista, se le asignará el número 0x7f050000 como identificador (usando setId).

Si más adelante usamos una cadena como @id/principal en otro atributo, el compilador reconocerá el nombre del recurso y lo remplazará por el mismo valor numérico, 0x7f050000, para pasárselo al método que se corresponda con el atributo.

Otras propiedades

La clase View proporciona otras muchas propiedades a las vistas. Veamos algunas de las más habituales:

  • android:background (setBackgroundResource, setBackgroundDrawable, setBackgroundColor): permite reemplazar el fondo predeterminado de la vista por un color uniforme o una imagen.

  • android:clickable (setClickable): determina si la vista responderá a pulsaciones sobre ella o no. Se puede hacer que cualquier vista responda a pulsaciones aunque no sea su cometido habitual.

  • android:minWidth (setMinimumHeight) y android:minHeight (setMaximumHeight): permiten especificar un tamaño mínimo (ancho y alto) para la vista. Como los tamaños de pantalla de los dispositivos Android son muy variados y los diseños tienden a adaptarse a ellos, no se suelen fijar los tamaños de las vistas. Pero sí se puede establecer un mínimo que garantice la correcta presentación de su contenido.

  • android:padding (setPadding): el padding es un espacio alrededor de los límites de la vista, pero dentro de ella, que el contenido no puede ocupar. El padding sirve para alejar el contenido de los bordes de la vista, evitando que quede demasiado cerca o pegado a los contenidos de las vistas circundantes. Además del atributo android:padding, que permite definir el padding para los cuatro bordes de la vista simultáneamente, hay atributos particulares para cada borde: paddingLeft, paddingRight, paddingTop y paddingBottom. En este post explico con más detalle el padding y sus diferencias con el margen.

  • android:visibility (setVisibility): permite controlar la visibilidad de la vista. Tiene tres posibles valores:

    • visible: la vista se muestra en la actividad. Es el estado predeterminado.

    • invisible: la vista no se muestra en la actividad, pero el diseño la tiene en cuenta. El resultado es que se deja sin ocupar el espacio que ocuparía si fuera visible.

    • gone: la vista ni se muestra ni la tiene en cuenta el diseño. Es como si no se hubiera incluido nunca en la actividad. Sin embargo, la vista existe y sus propiedades son accesibles.

Luego, cada vista concreta añade sus propias propiedades. Por ejemplo, <TextView> y <Button> disponen de un atributo android:text, que permite establecer el texto que mostrarán. En futuros posts iremos viendo las propiedades particulares de diferentes diseños y widgets.

Recuerda que en esta página dispones de enlaces a todos los artículos sobre Android que he publicado en La columna 80.

Una respuesta to “Diseñando interfaces en Android: XML y propiedades”

  1. @tarmuta said

    Gracia, muy bueno tus post, es justo y preciso lo que un desarrollador que viene de otro lenguaje o plataforma , necesita saber . nuevamente Gracias.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s