La vista ListView (introducción)

Una vista ListView visualiza una lista deslizable verticalmente de varios elementos, donde cada elemento puede definirse como un Layout .Su utilización es algo compleja, pero muy potente. Un ejemplo lo podemos ver en la siguiente figura:

Definir un ListView conlleva los siguientes cuatro pasos:

  • Diseñar un Layout que lo contenga al ListView
  • Diseñar un Layout individual que se repetirá en la lista
  • Implementar una actividad que lo visualice el Layout con el ListView
  • Personalizar cada una de los Layouts individuales según nuestros datos  

Veamos estos pasos con más detalle:

Para utilizar un ListView dentro de un Layout puedes usar la siguiente estructura:

<FrameLayout>
       <ListView
              android:id="@android:id/list"
              ... />
       <TextView
              android:id="@android:id/empty"
              ... />
</FrameLayout>

Donde tenemos un FrameLayout que permite visualizar dos posibles elementos, uno u otro, pero no los dos simultáneamente. El primero es el ListView que se visualizará cuando haya algún elemento en la lista. El segundo puede ser cualquier tipo de vista y se visualizará cuando no existan elementos en la lista. El sistema controla la visibilidad de forma automática, solo has de tener cuidado de identificar cada uno de los elementos con el valor exacto que se muestra.

NOTA: Recuerda que para crear nuevos identificadores debes utilizar la expresión"@+id/nombre_identificador". El carácter @ significa que se trata de un identificador de recurso que se definirá en la clase R.java. El carácter + significa que el recurso ha de ser creado en este momento. En este caso hemos utilizado identificadores definidos en el sistema (es decir @android:significa que es un recurso definido en la clase android.R.java).

Una vez creado el Layout que contiene el ListView tendremos que visualizarlo en una actividad. Para este propósito utilizaremos un  tipo de actividad especial,  ListActivity.

También tendremos que indicar al sistema cada uno de los Layouts individuales que contendrá el ListView. Esto lo haremos llamando al método setListAdapter(). Existen varias alternativas con diferentes grados de dificultad. Para una mejor conprensión iremos mostrando tres ejemplos de uso de setListAdapter(), de más sencillo a más complejo.

Las capturas anteriores muestran los tres ListView que vamos a construir. El de la izquierda se limita a mostrar una lista de Strings. El del centro visualiza una lista de un Layout diseñado por nosotros. Aunque este Layout tiene varios componentes (una imagen, dos textos y RatingBar), solo cambiamos uno de los textos. En el último ejemplo cambiaremos todos los componentes.

video[Tutorial] Uso de ListView

Un ListView con textos

La actividad inicial de la aplicación Mis Lugares nos permite escoger entre cuatro botones. En una aplicación como la desarrollada, sería mucho más interesante que en esta actividad se visualizaran directamente una lista con los lugares almacenados.

NOTA: En los siguientes ejercicios resolveremos el problema de una forma ligeramente distinta a la explicada anteriormente y en el vídeo.

Ejercicio paso a paso: Un ListView con textos en la actividad principal de Mis Lugares

1.     Reemplaza el contenido del layout content_main.xml por siguiente código:

<LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
   <ListView
       android:id="@+id/listView"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:drawSelectorOnTop="false" />
</LinearLayout>

Observa como en este caso el id es definido por nosotros, en lugar de utilizar un id del sistema.

2.     En la práctica “Recursos alternativos en Mis Lugares” se crea un recurso alternativo para este layout en res/layout-land/content_main.xml. Elimina este recurso alternativo.

3.     Añade en la actividad MainActivity el código subrayado:

public class MainActivity extends AppCompatActivity {
    ...
    public BaseAdapter adaptador;
    @Override 
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        adaptador = new ArrayAdapter(this, 
                           android.R.layout.simple_list_item_1, 
                           MainActivity.lugares.listaNombres()); 
        ListView listView = (ListView) findViewById(R.id.listView);
        listView.setAdapter(adaptador);
    }
    ...

En este caso, la actividad no hereda de ListActivity, si no que el ListView es incorporado como un elemento dentro de la actividad. Además, ha de llamar al método setAdapter() para indicar el adaptador con la lista de elementos a visualizar. En el ejemplo se ha utilizado una de la posibilidades más sencillas, para crear un adaptador, usar la clase ArrayAdapter<clase>. Un ArrayAdapter crea las vistas del ListView a partir de los datos almacenados en un array. Puedes utilizar un array que contenga datos de cualquier clase, no tienes más que indicar en <Clase> la clase deseada. En este caso se utiliza de un array de String[1]. El constructor de ArrayAdapter<clase> tiene tres parámetros: El primer parámetro es un Context con información sobre el entorno de la aplicación. Utilizaremos como contexto la misma actividad que hace la llamada. El segundo parámetro es un Layout, utilizado para representar cada elemento de la lista. En este ejemplo, en lugar de definir uno nuevo, utilizaremos una ya definido en el sistema. El último parámetro es un array con los string a mostrar. Para ello, llamamos al método listaNombres() que nos devuelve una lista con todos los nombres de los lugares.

4.     Elimina del método onCreate() el código destinado a inicializar los botones. 

5.     Añade a la interfaz  Lugares el siguiente método:

List listaNombres() //Devuelve un ArrayList con todos los elementos

5.     Añade el siguiente método a la clase LugaresVector.

public List listaNombres(){
       ArrayList resultado = new ArrayList();
       for (Lugar lugar:vectorLugares){
             resultado.add(lugar.getNombre());
       }
       return resultado;
}

6.     En un ejercicio anterior habíamos aplicado un estilo para que no se mostrara la barra de acciones. Elimina este atributo en AndroidManifest.xml para que vuelva a aparecer la barra de acciones:

<application
     …
     <activity
          android:name="com.example.mislugares.MainActivity"
          android:label="@string/app_name"
          android:theme="@android:style/AppTheme.NoTitleBar.Fullscreen">
          …

7.       Prueba si funcionan las modificaciones introducidas.

Un ListView que visualiza Layouts personalizados

Vamos a personalizar el ListView anterior para que cada elemento de la lista sea un layout definido por nosotros.Tal y como se muestra en la siguiente captura de pantalla, solo modificaremos un campo de la vista (el nombre del lugar).

Ejercicio paso a paso: Un ListView que visualiza layouts personalizados

1.     Reemplaza en código subrayando del ejercicio anterior por:

adaptador = new ArrayAdapter(this,
       R.layout.elemento_lista,
       R.id.nombre,
       Lugares.listaNombres());

Como hemos explicado, la clase ArrayAdapter<String> permite insertar los datos desde un array de String en nuestro ListView. En este ejemplo se utiliza un constructor con cuatro parámetros:

this: es el contexto, con información sobre el entorno de la aplicación.

R.layout.elemento_lista: es una referencia de recurso a la vista que será utilizada repetidas veces para formar la lista. Se define a continuación.

R.id. titulo:  identifica un id de la vista anterior que ha de ser un TextView. Su texto será reemplazado por el que se indica en el siguiente parámetro.

MainActivity.lugares.listaNombres(): vector de String con los textos que serán visualizados en cada uno de los TextView.

2.     Ahora hemos de definir el layout que representará cada uno de los elementos de la lista. Crea el fichero res/Layout/elemento_lista.xml con el siguiente código:

<RelativeLayout  
       xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="match_parent"
       android:layout_height="?android:attr/listPreferredItemHeight">
       <ImageView 
             android:id="@+id/foto"
             android:layout_width="?android:attr/listPreferredItemHeight"
             android:layout_height="?android:attr/listPreferredItemHeight"
             android:layout_alignParentLeft="true"
             android:contentDescription="fotografía"
             android:src="@drawable/bar"/>
       <TextView 
             android:id="@+id/nombre"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="Nombres del lugar"
             android:layout_toRightOf="@id/foto"
             android:layout_alignParentTop="true"
             android:textAppearance="?android:attr/textAppearanceMedium"
             android:singleLine="true" />
       <TextView 
             android:id="@+id/direccion"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_below="@id/nombre"
             android:layout_toRightOf="@id/foto"
             android:gravity="center"
             android:singleLine="true"
             android:text="dirección del lugar"/>
       <RatingBar
             android:id="@+id/valoracion"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_alignParentBottom="true"
             android:layout_below="@id/direccion"
             android:layout_toRightOf="@id/foto"
             style="?android:attr/ratingBarStyleSmall"
             android:isIndicator="true"
             android:rating="3"/>
</RelativeLayout>

Para combinar las vistas se ha escogido un RelativeLayout, su alto se establece a partir de un parámetro de configuración del sistema ?android:attr/listPreferredItemHeight (alto preferido para item de lista). El primer elemento que contiene es un ImageView alineado a la izquierda. Su alto es la misma que el contenedor (match_parent) y su ancho se establece con el mismo parámetro que el alto del contenedor. Por lo tanto la imagen será cuadrada. A la derecha se muestran  dos textos. En el texto de mayor tamaño se visualizará para el nombre del lugar y en el de menor tamaño la dirección. Bajo estos textos se ha incluido un RatingBar.

3.     Ejecuta la aplicación y verifica el resultado.

[1] Para saber más sobre clases genéricas puedes ver el siguiente Polimedia http://youtu.be/N3yy2pfUaE0.