Facebook Fanpage


lunes, 1 de octubre de 2012

Interfaz de usuario en Android: Controles de selección - II


En el artículo anterior ya comenzamos a hablar de los controles de selección en Android, empezando por explicar el concepto de adaptador y describiendo el control Spinner. En este nuevo artículo nos vamos a centrar en el control de selección más utilizado de todos, elListView.
Un control ListView muestra al usuario una lista de opciones seleccionables directamente sobre el propio control, sin listas emergentes como en el caso del control Spinner. En caso de existir más opciones de las que se pueden mostrar sobre el control se podrá por supuesto hacerscroll sobre la lista para acceder al resto de elementos.
Para empezar, veamos como podemos añadir un control ListView a nuestra interfaz de usuario:
<ListView android:id="@+id/LstOpciones"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

Una vez más, podremos modificar el aspecto del control utilizando las propiedades de fuente y color ya comentadas en artículos anteriores. Por su parte, para enlazar los datos con el control podemos utlizar por ejemplo el mismo código que ya vimos para el control Spinner. Definiremos primero un array con nuestros datos de prueba, crearemos posteriormente el adaptador de tipoArrayAdapter y lo asignaremos finalmente al control mediante el método setAdapter():


final String[] datos =
    new String[]{"Elem1","Elem2","Elem3","Elem4","Elem5"};
 
ArrayAdapter adaptador =
    new ArrayAdapter(this,
        android.R.layout.simple_list_item_1, datos);
 
ListView lstOpciones = (ListView)findViewById(R.id.LstOpciones);
 
lstOpciones.setAdapter(adaptador);

En este caso, para mostrar los datos de cada elemento hemos utilizado otro layout genérico de Android para los controles de tipo ListView (android.R.layout.simple_list_item_1), formado únicamente por un TextView con unas dimensiones determinadas. La lista creada quedaría como se muestra en la imagen siguiente:




Como podéis comprobar el uso básico del control ListView es completamente análogo al ya comentado para el control Spinner.
Si quisiéramos realizar cualquier acción al pulsarse sobre un elemento de la lista creada tendremos que implementar el evento onItemClick. Veamos cómo con un ejemplo:

lstOpciones.setOnItemClickListener(new OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView a, View v, int position, long id) {
        //Acciones necesarias al hacer click
    }
});


Hasta aquí todo sencillo. Pero, ¿y si necesitamos mostrar datos más complejos en la lista? ¿qué ocurre si necesitamos que cada elemento de la lista esté formado a su vez por varios elementos? Pues vamos a provechar este artículo dedicado a los ListView para ver cómo podríamos conseguirlo, aunque todo lo que comentaré es extensible a otros controles de selección.
Para no complicar mucho el tema vamos a hacer que cada elemento de la lista muestre por ejemplo dos líneas de texto a modo de título y subtítulo con formatos diferentes (por supuesto se podrían añadir muchos más elementos, por ejemplo imágenes, checkboxes, etc).
En primer lugar vamos a crear una nueva clase java para contener nuestros datos de prueba. Vamos a llamarla Titular y tan sólo va a contener dos atributos, título y subtítulo.
package net.sgoliver;
 
public class Titular
{
    private String titulo;
    private String subtitulo;
 
    public Titular(String tit, String sub){
        titulo = tit;
        subtitulo = sub;
    }
 
    public String getTitulo(){
        return titulo;
    }
 
    public String getSubtitulo(){
        return subtitulo;
    }
}

En cada elemento de la lista queremos mostrar ambos datos, por lo que el siguiente paso será crear un layout XML con la estructura que deseemos. En mi caso voy a mostrarlos en dos etiquetas de texto (TextView), la primera de ellas en negrita y con un tamaño de letra un poco mayor. Llamaremos a este layout “listitem_titular.xml“:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:orientation="vertical">
<TextView android:id="@+id/LblTitulo"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textStyle="bold"
    android:textSize="20px" />
<TextView android:id="@+id/LblSubTitulo"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textStyle="normal"
    android:textSize="12px" />
</LinearLayout>

Ahora que ya tenemos creados tanto el soporte para nuestros datos como el layout que necesitamos para visualizarlos, lo siguiente que debemos hacer será indicarle al adaptador cómo debe utilizar ambas cosas para generar nuestra interfaz de usuario final. Para ello vamos a crear nuestro propio adaptador extendiendo de la clase ArrayAdapter.


class AdaptadorTitulares extends ArrayAdapter {
 
    Activity context;
 
        AdaptadorTitulares(Activity context) {
            super(context, R.layout.listitem_titular, datos);
            this.context = context;
        }
 
        public View getView(int position, View convertView, ViewGroup parent) {
        LayoutInflater inflater = context.getLayoutInflater();
        View item = inflater.inflate(R.layout.listitem_titular, null);
 
        TextView lblTitulo = (TextView)item.findViewById(R.id.LblTitulo);
        lblTitulo.setText(datos[position].getTitulo());
 
        TextView lblSubtitulo = (TextView)item.findViewById(R.id.LblSubTitulo);
        lblSubtitulo.setText(datos[position].getSubtitulo());
 
        return(item);
    }
}


Analicemos el código anterior. Lo primero que encontramos es el constructor para nuestro adaptador, al que sólo pasaremos el contexto (que será la actividad desde la que se crea el adaptador). En este constructor tan sólo guardaremos el contexto para nuestro uso posterior y llamaremos al constructor padre tal como ya vimos al principio de este artículo, pasándole el ID del layout que queremos utilizar (en nuestro caso el nuevo que hemos creado, “listitem_titular”) y el array que contiene los datos a mostrar.
Posteriormente, redefinimos el método encargado de generar y rellenar con nuestros datos todos los controles necesarios de la interfaz gráfica de cada elemento de la lista. Este método esgetView().
El método getView() se llamará cada vez que haya que mostrar un elemento de la lista. Lo primero que debe hacer es “inflar” el layout XML que hemos creado. Esto consiste en consultar el XML de nuestro layout y crear e inicializar la estructura de objetos java equivalente. Para ello, crearemos un nuevo objeto LayoutInflater y generaremos la estructura de objetos mediante su método inflate(id_layout).
Tras esto, tan sólo tendremos que obtener la referencia a cada una de nuestras etiquetas como siempre lo hemos hecho y asignar su texto correspondiente según los datos de nuestro array y la posición del elemento actual (parámetro position del método getView()).
Una vez tenemos definido el comportamiento de nuestro adaptador la forma de proceder en la actividad principal será análoga a lo ya comentado, definiremos el array de datos de prueba, crearemos el adaptador y lo asignaremos al control mediante setAdapter():
private Titular[] datos =
    new Titular[]{
        new Titular("Título 1", "Subtítulo largo 1"),
        new Titular("Título 2", "Subtítulo largo 2"),
        new Titular("Título 3", "Subtítulo largo 3"),
        new Titular("Título 4", "Subtítulo largo 4"),
        new Titular("Título 5", "Subtítulo largo 5")};
 
//...
//...
 
AdaptadorTitulares adaptador =
        new AdaptadorTitulares(this);
 
ListView lstOpciones = (ListView)findViewById(R.id.LstOpciones);
 
lstOpciones.setAdapter(adaptador);
Hecho esto, y si todo ha ido bien, nuestra nueva lista debería quedar como vemos en la imagen siguiente:



Podéis descargar el código de este artículo desde este enlace.

Descargar Código Fuente    Opción2

Interfaz de usuario en Android: Controles de selección - I

Una vez repasados los controles básicos (I, II, III) que podemos utilizar en nuestras aplicaciones Android, vamos a dedicar los próximos artículos a describir los diferentes controles de selección disponibles en la plataforma. Al igual que en otros frameworks Android dispone de diversos controles que nos permiten seleccionar una opción dentro de una lista de posibilidades. Así, podremos utilizar listas desplegables (Spinner), listas fijas (ListView), tablas (GridView) y otros controles específicos de la plataforma como por ejemplo las galerías de imágenes (Gallery).

 En este primer artículo dedicado a los controles de selección vamos a describir un elemento importante y común a todos ellos, los adaptadores, y lo vamos a aplicar al primer control de los indicados, las listas desplegables.

 Adaptadores en Android (adapters) 

 Para los desarrolladores de java que hayan utilizado frameworks de interfaz gráfica como Swing, el concepto de adaptador les resultará familiar. Un adaptador representa algo así como una interfaz común al modelo de datos que existe por detrás de todos los controles de selección que hemos comentado. Dicho de otra forma, todos los controles de selección accederán a los datos que contienen a través de un adaptador.

 Además de proveer de datos a los controles visuales, el adaptador también será responsable de generar a partir de estos datos las vistas específicas que se mostrarán dentro del control de selección. Por ejemplo, si cada elemento de una lista estuviera formado a su vez por una imagen y varias etiquetas, el responsable de generar y establecer el contenido de todos estos “sub-elementos” a partir de los datos será el propio adaptador.

 Android proporciona de serie varios tipos de adaptadores sencillos, aunque podemos extender su funcionalidad facilmente para adaptarlos a nuestras necesidades. Los más comunes son los siguientes:


  •  ArrayAdapter. Es el más sencillo de todos los adaptadores, y provee de datos a un control de selección a partir de un array de objetos de cualquier tipo. 
  • SimpleAdapter. Se utiliza para mapear datos sobre los diferentes controles definidos en un fichero XML de layout. 
  • SimpleCursorAdapter. Se utiliza para mapear las columnas de un cursor sobre los diferentes elementos visuales contenidos en el control de selección. Para no complicar excesivamente los tutoriales, por ahora nos vamos a conformar con describir la forma de utilizar un ArrayAdapter con los diferentes controles de selección disponibles. Más adelante aprenderemos a utilizar el resto de adaptadores en contextos más específicos. 
 Veamos cómo crear un adaptador de tipo ArrayAdapter para trabajar con un array genérico de java:


final String[] datos =
        new String[]{"Elem1","Elem2","Elem3","Elem4","Elem5"};
ArrayAdapter adaptador =
        new ArrayAdapter(this,
            android.R.layout.simple_spinner_item, datos);



Comentemos un poco el código.
Sobre la primera línea no hay nada que decir, es tan sólo la definición del array java que contendrá los datos a mostrar en el control, en este caso un array sencillo con cinco cadenas de caracteres. En la segunda línea creamos el adaptador en sí, al que pasamos 3 parámetros:

  1.  El contexto, que normalmente será simplemente una referencia a la actividad donde se crea el adaptador. 
  2. El ID del layout sobre el que se mostrarán los datos del control. En este caso le pasamos el ID de un layout predefinido en Android (android.R.layout.simple_spinner_item), formado únicamente por un control TextView, pero podríamos pasarle el ID de cualquier layout de nuestro proyecto con cualquier estructura y conjunto de controles, más adelante veremos cómo. 
  3. El array que contiene los datos a mostrar. 

Con esto ya tendríamos creado nuestro adaptador para los datos a mostrar y ya tan sólo nos quedaría asignar este adaptador a nuestro control de selección para que éste mostrase los datos en la aplicación.

 Control Spinner [API] 

 Las listas desplegables en Android se llaman Spinner. Funcionan de forma similar al de cualquier control de este tipo, el usuario selecciona la lista, se muestra una especie de lista emergente al usuario con todas las opciones disponibles y al seleccionarse una de ellas ésta queda fijada en el control. Para añadir una lista de este tipo a nuestra aplicación podemos utilizar el código siguiente:


< Spinner android:id="@+id/CmbOpciones"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" />


Poco vamos a comentar de aquí ya que lo que nos interesan realmente son los datos a mostrar. En cualquier caso, las opciones para personalizar el aspecto visual del control (fondo, color y tamaño de fuente, …) son las mismas ya comentadas para los controles básicos.

 Para enlazar nuestro adaptador (y por tanto nuestros datos) a este control utilizaremos el siguiente código java:


final Spinner cmbOpciones = (Spinner)findViewById(R.id.CmbOpciones);
adaptador.setDropDownViewResource(
        android.R.layout.simple_spinner_dropdown_item);
cmbOpciones.setAdapter(adaptador);


Comenzamos como siempre por obtener una referencia al control a través de su ID. Y en la última línea asignamos el adaptador al control mediante el método setAdapter(). 
¿Y la segunda línea para qué es? Cuando indicamos en el apartado anterior cómo construir un adaptador vimos cómo uno de los parámetros que le pasábamos era el ID del layout que utilizaríamos para visualizar los elementos del control. Sin embargo, en el caso del control Spinner, este layout tan sólo se aplicará al elemento seleccionado en la lista, es decir, al que se muestra directamente sobre el propio control cuando no está desplegado. 
Sin embargo, antes indicamos que el funcionamiento normal del control Spinner incluye entre otras cosas mostrar una lista emergente con todas las opciones disponibles. Pues bien, para personalizar también el aspecto de cada elemento en dicha lista emergente tenemos el método setDropDownViewResource(ID_layout), al que podemos pasar otro ID de layout distinto al primero sobre el que se mostrarán los elementos de la lista emergente. En este caso hemos utilizado otro layout predefinido an Android para las listas desplegables (android.R.layout.simple_spinner_dropdown_item).


Con estas simples lineas de código conseguiremos mostrar un control como el que vemos en las siguientes imágenes:



Como se puede observar en las imágenes, la representación del elemento seleccionado (primera imagen) y el de las opciones disponibles (segunda imagen) es distinto, incluyendo el segundo de ellos incluso algún elemento gráfico a la derecha para mostrar el estado de cada opción. Como hemos comentado, esto es debido a la utilización de dos layouts diferentes para uno y otros elementos. 

 En cuanto a los eventos lanzados por el control Spinner, el más comunmente utilizado será el generado al seleccionarse una opción de la lista desplegable, onItemSelected
Para capturar este evento se procederá de forma similar a lo ya visto para otros controles anteriormente, asignadole su controlador mediante el método setOnItemSelectedListener():


cmbOpciones.setOnItemSelectedListener(
        new AdapterView.OnItemSelectedListener() {
        public void onItemSelected(AdapterView parent,
            android.view.View v, int position, long id) {
                lblMensaje.setText("Seleccionado: " + datos[position]);
        }
        public void onNothingSelected(AdapterView parent) {
            lblMensaje.setText("");
        }
});

Para este evento definimos dos métodos, el primero de ellos (onItemSelected) que será llamado cada vez que se selecciones una opción en la lista desplegable, y el segundo (onNothingSelected) que se llamará cuando no haya ninguna opción seleccionada (esto puede ocurrir por ejemplo si el adaptador no tiene datos).

 Podéis descargar el código fuente de este artículo pulsando aquí.

 En el siguiente artículo describiremos el uso de controles de tipo lista (ListView).

Descargar Código Fuente         Opción2