Interfaz gráfica: Actividades, layouts y eventos
Desde el punto de vista de un usuario una aplicación está formada por un conjunto de pantallas que permiten la interacción. En Android estas pantallas reciben el nombre de actividades y son objetos que extienden a la clase Activity. Las actividades tienen un elevado grado de autonomía, cada una se encarga de los componentes que contiene, y desde una aplicación se pueden invocar actividades de otra aplicación.
Los componentes que se visualizan en una actividad extienden a la clase View. Y aquellos componentes que permiten agrupar a otros extienden a ViewGroup. Así un Button, o un CheckBox son componentes que extienden a View y un LinearLayout o TableLayout son componentes que extienden a ViewGroup.
Los layouts son los elementos que permiten distribuir los componentes con una disposición determinada. Por ejemplo, el LinearLayout los presenta en fila (vertical u horizontal) y el TableLayout en forma de tabla (con las filas y columnas que especifiquemos).
Definición de layouts mediante ficheros XML
Al desarrollar para Android se nos anima a declarar las actividades, con sus correspondientes elementos, en ficheros XML independientes del código. De esta manera se gana en flexibilidad y claridad. Normalmente es muy tedioso escribir el código que construye la interfaz gráfica instanciando cada uno de los objetos y agrupándolos de una manera específica. Además cuesta bastante trabajo hacer cambios para probar diferentes disposiciones gráficas.
Cuando se utiliza un fichero externo para definir una interfaz se evitan estas molestias. El programador símplemente describe cómo se agrupan los elementos de la actividad utilizando etiquetas XML de un modo muy similar al utilizado en el desarrollo web. Entonces, las herramientas de desarrollo interpretan este fichero y generan el código necesario.
Las ventajas de este esquema son:
- Simplicidad y claridad en la descripción de las interficies gráficas
- Es fácil y rápido cambiar la disposición gráfica
- Se separa el código de la aplicación de la representación gráfica de la interfaz
Evidentemente sigue siendo posible generar la interfaz de la manera tradicional, o bien esto será una necesidad cuando la interfaz sea dinámica y los elementos se deban generar al vuelo durante la ejecución del programa. Pero el nuevo enfoque en general resulta muy práctico y cómodo. Además se extiende a otros apartados, también se nos anima a separar todas las cadenas de nuestro programa en un fichero de recursos (lo que facilita su edición o internacionalización).
Los ficheros XML que definen la interfaz de una actividad son recursos que se pueden encontrar en la carpeta res/layout de nuestro proyecto. Cuando se crea un nuevo proyecto en Eclipse, se crea automáticamente el fichero res/layout/main.xml, nosotros podemos editar este fichero para cambiar la interfaz de la aplicación.
Por ejemplo con:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, I am a TextView" />
<Button android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, I am a Button" />
</LinearLayout>
Se define un layout vertical que presenta un TextView y un Button. El nombre de las etiquetas se corresponde con las clases que representan y los atributos con sus propiedades (la mayoría autodescriptivas). Es importante fijarse en la sintáxis del atributo id utilizado para definir el identificador del elemento (el TextView y el Button). La @ indica que se trata de un recurso, el + de que es nuevo y debe ser añadido y 'text' o 'button' será el nombre del identificador.
Los recursos quedan registrados en la clase R.java que las herramientas de desarrollo construyen de manera automática bajo la carpeta gen. Allí se referencian mediante constantes los diferentes valores. Así R.id.button tendrá un valor que identificará a nuestro botón y R.id.text otro para identificar al TextView. La clase R.java también permite referenciar otros elementos como las cadenas utilizadas que se guardan en res/values/strings.xml.
Gestionando eventos
Cuando el usuario interacciona con la interfaz, y por ejemplo pulsa un botón, se generan eventos. La aplicación debe especificar qué acciones se deben llevar a cabo cuando se producen determinados eventos. A los objetos que están pendientes de los eventos que se producen se los llama Listeners. Para cada componente que pueda generar un evento, como un botón o un campo de texto, es posible especificar a qué objeto (Listener) se le notificarán los eventos que produzca.
La clase View define interfaces que deben cumplir los diferentes tipos de Listeners:
- View.OnClickListener
- Para procesar notificaciones de click
- View.OnLongClickListener
- Para procesar las notificaciones de click mantenido
- View.OnFocusChangeListener
- Para procesar la adquisición o pérdida del foco
- View.OnKeyListener
- Para procesar las pulsaciones de teclas físicas del dispositivo. Sólo se reciben estas notificaciones si el elemento tiene el foco mientras se realiza la pulsación.
- View.OnTouchListener
- Cuando el usuario toca, pulsando o liberando, un elemento.
- View.OnCreateContextMenuListener
- Cuando se crea un menú contextual porque el usuario ha mantenido una pulsación larga
Cada una de estas interfaces declara un método que es llamado cuando se recibe el evento. Al implementar dicho método el programador puede especificar cómo se gestiona el evento.
Así para reaccionar a la pulsación del botón declarado anteriormente será necesario:
- Instanciar un objeto que implemente la interfaz View.OnClickListener
- Asignar dicho objeto como OnClickListener del botón
Ambas operaciones pueden realizarse en el método onCreate de nuestra actividad. Una manera compacta de realizarlo, cuando el manejador del evento no es demasiado extenso, consiste en asignar el Listener utilizando una clase anónima que implemente la interfaz.
public class DeclaringLayout extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button button = (Button)findViewById(R.id.button); button.setOnClickListener(new OnClickListener() { public void onClick(View v) { Button button = (Button)findViewById(R.id.button); button.setText("hola"); } }); } }
Obsérvese lo práctico que resulta el método Activity.findViewById(int id) para obtener una referencia a cualquier View de nuestra actividad.
Ejercicios:
- Construya una nueva aplicación llamada CuentaClicks. La aplicación mostrará un botón y un contador que inicialmente tendrá el valor 0, cada vez que se pulse el botón se incrementará el contador.
- Intente crear una aplicación que muestre diferentes adagios. Cuando se lance la aplicación se mostrará un adagio al azar junto a un botón para obtener otro diferente, cada pulsación del botón hará que se muestre otro adagio al azar. Los adagios estarán representados por un array de cadenas en el código.
- Escriba la aplicación CurrencyConverter que permita convertir moneda entre € y $.