Inicio > Android, Java, Programación > ListView y ArrayAdapter en Android

ListView y ArrayAdapter en Android


Bueno, como habréis notado, he pasado un poco de los videotutoriales pues son muy costosos de hacer y aburridos de ver, así que vuelvo a los tutoriales normales. En este caso, como usar los ListView y por qué.

¿Qué es un ListView?

Un ListView es un sistema para mostrar una gran cantidad de elementos en forma de lista, como pueden ser mensajes de un foro, emails, estadísticas… Sus principales ventajas son dos: es muy cómodo de usar y es muy eficiente.

Cómodo de Usar:

En los ListView no tendremos que ir añadiendo cada elemento, si no que definiremos en una clase que extienda de View (o una subclase de esta) como ha de representarse el elemento e insertaremos todos los elementos en alguna estructura de datos, como un ArrayList, un Vector o un Array, eso ya es al gusto de cada uno, aunque se recomienda sistemas con acceso directo a elementos de una posición dada, así pues, los LinkedLists no son muy recomendables.

Eficiente:

A diferencia de un ScrollView o sistemas semejantes, los ListView no crean tantos Views como elementos a representar, si no que crean tantos como puedan ser visibles por pantalla de forma simultánea, y conforme vamos moviéndonos por la lista va cambiándole los valores a mostrar, ahorrando muchísima memoria en el sistema, pues, por ejemplo, aún teniendo una lista de 200 elementos solo tenemos que crear 5 views e ir cambiándole los valores según cuales estén visibles.

Código, dame código:

Bien, ahora que sabemos como funciona el sistema vamos  a ver como podemos hacerlo. Lo primero que debemos saber es que Android implementa ya algunos view y adapter para las opciones más comunes, como mostrar un texto. En estos casos, basta con pasarle un ArrayAdapter del tipo concreto y el layout a usar. En nuestro ejemplo, mostraremos los datos de un rectángulo:

/**
* Android dispone de algunos Adapter predefinidos.
 * En este caso, usaremos uno para representar una ListView de TextView
 * con los strings de un array.
 *
 * Es tan simple como pasarle el contexto, el layout a usar y el array.
 */

 String strings[] = new String[100];
 for (int i = 0; i < strings.length; i++)
 strings[i] = new String("Elemento "+i);

 ListView lvstring = (ListView) findViewById(R.id.lvstrings);
 lvstring.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, strings));

Con ese código ya tendríamos nuestro listado. Ahora bien, si queremos hacer algo más nuestro, la cosa se complica un poco.
Lo primero será crear un layout con nuestro view:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:layout_width="0px"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/base"
            />

         <TextView
            android:layout_width="0px"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+rectangulo/base"
            />

        <TextView
            android:layout_width="0px"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/altura"
            />

         <TextView
            android:layout_width="0px"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+rectangulo/altura"
            />

    </LinearLayout>

      <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:layout_width="0px"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/area"
            />

         <TextView
            android:layout_width="0px"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+rectangulo/area"
            />

        <TextView
            android:layout_width="0px"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/perimetro"
            />

         <TextView
            android:layout_width="0px"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+rectangulo/perimetro"
            />

    </LinearLayout>

</LinearLayout>

Ahora deberemos crear una clase que infle este layout y guarde los punteros a los elementos gráficos que tendremos que ir cambiando y un método para poder cambiar estos valores cómodamente. Para ahorrarnos trabajo, heredaremos directamente de LinearLayout e inflaremos el layout.

package com.miguelangellv.listview;

import android.content.Context;
import android.widget.LinearLayout;
import android.widget.TextView;

public class RectanguloView extends LinearLayout{

	private TextView base;
	private TextView altura;

	private TextView area;
	private TextView perimetro;

	public RectanguloView(Context context) {
		super(context);
		inflate(context, R.layout.rectanguloxml, this);

		/**
		 * Es muy importante guardar las direcciones de los elementos
		 * que vayamos a cambiar pues el findViewById requiere mucho tiempo
		 * y si cada vez que hacemos scroll tenemos que buscar todos los elementos
		 * cargaremos inecesariamente el terminal y perderemos fluidez
		 */

		base 		= (TextView) findViewById(R.rectangulo.base);
		area 		= (TextView) findViewById(R.rectangulo.area);
		altura 		= (TextView) findViewById(R.rectangulo.altura);
		perimetro 	= (TextView) findViewById(R.rectangulo.perimetro);
	}

	/**
	 * Este método nos permitirá asignar los valores a los diferentes
	 * componéntes gráficos según el objeto que queramos ver.
	 * @param rectangulo
	 */
	public void setRectangulo(Rectangulo rectangulo) {
		base.setText(""+rectangulo.getBase());
		area.setText(""+rectangulo.getArea());
		altura.setText(""+rectangulo.getAltura());
		perimetro.setText(""+rectangulo.getPerimetro());
	}

}

Bien, ya tenemos nuestro view listo, ahora solo nos queda realizar el adapter que lo utilice.

package com.miguelangellv.listview;

import java.util.ArrayList;

import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;

/**
 * Esta clase es un Adapter para representar Rectangulos en ListView.
 *
 * Lo que tendremos que hacer será heredar de BaseAdapter e implementar los métodos
 * que faltan.
 *
 * @author Miguel Ángel López
 */
public class RectanguloAdapter extends BaseAdapter {

	/**
	 * Aquí guardaremos todos los rectángulos que queremos
	 * representar en nuestro ListView. Es recomendable usar sistemas
	 * con acceso directo por posición, como el ArrayList, un Vector, un Array...
	 */
	private ArrayList<Rectangulo> rectangulos;

	/**
	 * El constructor
	 * @param rectangulos
	 */
	public RectanguloAdapter(ArrayList<Rectangulo> rectangulos) {
		this.rectangulos 	= rectangulos;

		//Cada vez que cambiamos los elementos debemos noficarlo
		notifyDataSetChanged();
	}

	/**
	 * Este método simplemente nos devuelve el número de
	 * elementos de nuestro ListView. Evidentemente es el tamaño del arraylist
	 */
	public int getCount() {
		return rectangulos.size();
	}

	/**
	 * Este método nos devuele el elemento de una posición determinada.
	 * El elemento es el Rectángulo, así que...
	 */
	public Object getItem(int position) {
		return rectangulos.get(position);
	}

	/**
	 * Aquí tenemos que devolver el ID del elemento. Del ELEMENTO, no del View.
	 * Por lo general esto no se usa, así que return 0 y listo.
	 */
	public long getItemId(int position) {
		return 0;
	}

	/**
	 * El método más complicado. Aquí tenemos que devolver el View a representar.
	 * En este método nos pasan 3 valores. El primero es la posición del elemento,
	 * el segundo es el View a utilizar que será uno que ya no esté visible y que
	 * lo reutilizaremos, y el último es el ViewGroup, es en nuestro caso, el ListView.
	 */

	public View getView(int position, View convertView, ViewGroup parent) {
		/**
		 * Lo primero que haremos es comprobar si el View ya existe y tenemos que reciclarlo
		 * o por el contrario debemos crear uno nuevo.
		 */

		RectanguloView view;
		if (convertView == null) //NO existe, creamos uno
			view = new RectanguloView(parent.getContext());
		else					//Existe, reutilizamos
			view = (RectanguloView) convertView;

		/**
		 * Ahora tenemos que darle los valores correctos, para ello usamos
		 * el método setRectangulo pasándole el rectángulo a mostrar
		 * y finalmente devolvemos el view.
		 */

		view.setRectangulo(rectangulos.get(position));

		return view;
	}
}

Una vez tenemos nuestro adapter ya solo nos queda insertarlo en nuestro ListView y listo.

/**
* Para crear nosotros un ListView con un diseño propio deberemos crear un Adapter.
* En nuestro caso vamos a mostrar la información de un Rectángulo definido en la clase Rectángulo.
* Para representarlo usaremos un RectanguloView y finalmente como Adapter usaremos RectanguloAdapter.
*
*/

ArrayList<Rectangulo> rectangulos = new ArrayList<Rectangulo>(100);
for (int i = 0; i < 100; i++)
rectangulos.add(new Rectangulo());

ListView lvrectangulo = (ListView) findViewById(R.id.lvpersonal);
lvrectangulo.setAdapter(new RectanguloAdapter(rectangulos));

Finalmente, si queréis el código completo, lo tenéis aquí.
Para cualquier duda, preguntadme.

Anuncios
  1. 6 mayo, 2012 en 10:45 pm

    Muchas gracias por el tutorial!!

    Por fin he conseguido que funcione, aunque el tema de los adapters lo estaba llevando bien: https://gist.github.com/2624292

    Lamentable…

  2. Ruben Escartin
    30 agosto, 2012 en 10:56 pm

    ¿Como defino el tipo de dato Rectangulo?

    • 30 agosto, 2012 en 10:57 pm

      Es un mero ejemplo. Debes usar el tipo que tú quieras y substituirlo por el de Rectángulo.

  3. Carol
    7 septiembre, 2012 en 1:04 pm

    Hola, para probar el funcionamiento antes de modificar el código estaría bien que publicaras tu definición de la clase Rectangulo. Perdón por la molestia. Muchas gracias por el tuto.

    • 7 septiembre, 2012 en 1:08 pm

      Es un ejemplo, ni si quiera lo definí. Pero sería algo así:

      public class Rectangulo() {

      private int altura;
      private int base;

      public Rectangulo() {
      Random random = new Random();
      altura = random.nexInt(50)+1;
      base = random.nextInt(50)+1;

      }

      public int getAltura() {
      return altura;
      }

      public int getBase() {

      return base;
      }

      public int getArea() {
      return base * altura;
      }

      public int getPerimetro() {
      return base*2 + altura*2:
      }

      }

  4. Jose Maria
    19 diciembre, 2012 en 7:22 am

    Hola, gracias por el tutorial.
    Solo tengo una duda, el efecto de guardar los punteros a los elementos gráficos en la clase RectanguloView es equivalente a utilizar un “ViewHolder”.

    Gracias.

  5. Victor
    8 enero, 2014 en 6:34 pm

    Tengo una duda
    Me gustaráa poder agregar elementos a mi lista de forma dinámica, al agregarlo se envían los datos a un web service que me regresa una respuesta en base a eso yo pongo una imagen en la celda de ese elemento recién agregado.

    Las llamadas al web service ya están hechas, ya puedo agregar el elemento el problema es que cuando hago el scrolling mis imagenes se revuelven y no se como arreglarlo, se que es por el reciclado de views pero como puedo mantener de forma correcta mis elementos??

    • 8 enero, 2014 en 6:42 pm

      En el adapter debes tener un ArrayList u otro elemento de almacenaje de listas con todos los elementos a mostrar. Cuando recibes nueva información agregas un elemento en ese array y llamas al método notifydatasetchanged.

      Esto hará que todos los views se vuelvan a regenerar.

      Si agregas directamente los Views al ListView seguramente se hará todo un lío, debes añadirlo al array y que él solo, usando getView, vaya cogiendo las imágenes que correspondan.

  6. Jesús Salmerón
    28 enero, 2014 en 11:19 am

    Gracias por el magnífico tutorial.
    Un comentario: El link para la descarga del código no funciona.
    ¿Es posible solucionarlo?
    Saludos

  7. chencho
    16 diciembre, 2014 en 9:06 pm

    hola muchas gracias por el aporte.
    Tengo un pequeño problema … yo relleno el ArrayList con un webService en un AsynTask y en el método onPostExecute llamo a una función que instancia el adapter. Lo demas he seguido tu líena.

    Me da null pointer exception aquí:

    public void setRectangulo(Rectangulo rectangulo) {
    base.setText(“”+rectangulo.getBase());

    y te pongo un poco de mi código:

    /*Clase principal*/
    public class Inicio extends Activity {

    ArrayList listaServicios = new ArrayList();
    private ListView lvServicios = null;

    /*AsynTask*/
    protected void onPostExecute(String result) {
    progressDialog.setProgress(99);
    actualizarDisplay();
    }

    /*Metodo que rellena la lista*/
    public void actualizarDisplay()
    {
    progressDialog.dismiss();
    lvServicios.setAdapter(new ServicioAdapter(listaServicios));
    }

    Gracias si me puedes ayudar!!!

  1. No trackbacks yet.

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

A %d blogueros les gusta esto: