Desarrollo de Listas con SimpleAdapter

Continuando con los tutoriales sobre android explicaré como mostrar una lista de elementos compuestos, es decir, dónde en cada fila aparece más de un elemento.

Tabla de Contenidos

Diseñar formato de fila

Para empezar habrá que diseñar en XML la composición de las filas, en el formato en el que queremos mostrarlas, y guardaremos el resultado en la carpeta «layout», dándole un nombre que luego podamos recordar, en este caso «row.xml». El siguiente código es el utilizado en la aplicación de feria para mostrar la información de los diferentes eventos.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingRight="4px">
        <TextView android:id="@+id/hora"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="16dp"
            android:padding="3dp"
            />
    </LinearLayout>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingBottom="5dp">
        <TextView android:id="@+id/nombre"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20dp"
            />
        <TextView android:id="@+id/desc1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="14dp"
            />
    </LinearLayout>
</LinearLayout>

La mayor parte del código forma parte del diseño pero lo realmente importante, como esta señalado, son las id de cada uno de los campos que vamos a rellenar.

Diseño de lista

Ahora es cuando diseñamos la pantalla en la que se mostrará la lista. En el caso de la aplicación de feria, se muestra en la pate superior un menú de navegación, y dos botones en la parte inferior, pero puesto que no son de relevancia para el presente tutorial, no los incluyo en el código de ejemplo.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <ListView android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_weight="1"
        android:layout_height="fill_parent"
        android:padding="5dp"
        android:cacheColorHint="#00000000"></ListView>
    <TextView android:id="@id/android:empty"
        android:background="@null"
        android:text="NO SE ENCUENTRAN EVENTOS"
        android:layout_height="fill_parent"
        android:layout_width="fill_parent"
        />
</LinearLayout>

Lo verdaderamente importante de esta pantalla es el elemento ListView con id «@android:id/list», puesto que el cuadro de texto que aparecería en el caso de que la lista no tuviera elementos no tiene porque estar y del mismo modo tampoco tiene porque ser un cuadro de texto. Si quisiéramos que apareciera una imagen cuando la lista estuviera vacía tan solo habría que poner un elemento «ImageView» y asignarle la id «@id/android:empty».
Alrededor del listado se puede incluir cualquier otro tipo de objeto, y darle a ese listado cualquier propiedad que se quiera, siempre y cuando mantengamos la id del «ListView».

Código de ListActivity

Por último, tan solo queda rellenar la lista desde el código de nuestra actividad. Para ello nos valdremos de la clase «ListActivity» que nos proporcionará unos cuantos métodos bastante útiles para nuestro propósito. Para facilitar la tarea de «copy&paste» del código continúo el tutorial en el mismo:

public class Listado extends ListActivity {

	//Mantenemos un Array de elementos en la que podremos guardar
	//más información de la que mostraremos en el listado
	ArrayList < HashMap < String,
	String >> Eventos;

	//Con los siguientes Arrays establecemos la correspondencia
	//entre los elementos del Array de HashMaps de eventos (from)
	//con los elementos del diseño en XML de cada una de las filas (to)
	String[] from = new String[] {
		"Time",
		"Name",
		"Desc"
	};
	int[] to = new int[] {
		R.id.hora,
		R.id.nombre,
		R.id.desc1
	};

	@Override
	public void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);
		//Establecemos el diseño principal de la Actividad
		setContentView(R.layout.lista);

		// Este método de obtención de elementos puede cambiarse por cualquier otro
		//como leerlos de una BBDD o de un servidor web con JSON
		ArrayList < String[] > lista = new ArrayList < String[] > ();

		String[] evento1 = {
			"11:30",
			"Ofrenda de Flores",
			"Participa la Banda Sinfónica Municipal de Albacete",
			"1"
		};
		lista.add(evento1);

		String[] evento2 = {
			"12:00",
			"Los Redondeles",
			"La ronda de los Redondeles 2011",
			"2"
		};
		lista.add(evento2);

		String[] evento3 = {
			"12:00",
			"Futbol",
			"Albacete Balompie Vs. Tenerife C.D.",
			"3"
		};
		lista.add(evento3);

		// Transformamos los elementos String[] en HashMap para
		//posteriormente incluirlos en el Array Global que se utilizará
		//para rellenar la lista
		Eventos = new ArrayList < HashMap < String,
		String >> ();
		for (String[] evento: lista) {
			HashMap < String,
			String > datosEvento = new HashMap < String,
			String > ();

			// Aquí es dónde utilizamos las referencias creadas inicialmente
			//en el elemento "from"
			datosEvento.put("Time", evento[0]);
			datosEvento.put("Name", evento[1]);
			datosEvento.put("Desc", evento[2]);
			datosEvento.put("id", evento[3]);

			Eventos.add(datosEvento);
		}
	// Una vez tenemos toda la información necesaria para rellenar la lista
	//creamos un elemento que nos facilitará la tarea:
	//SimpleAdapter(Actividad, Array de HashMap con elementos, Fichero XML del
	//diseño de cada fila, Cadenas del HashMap, Ids del Fichero XML del diseño de cada fila)
		SimpleAdapter ListadoAdapter = 
                                    new SimpleAdapter(
                                                    this,
                                                    Eventos,
                                                    R.layout.row,
                                                    from, 
                                                    to
                                                );
		setListAdapter(ListadoAdapter);
	}
}

Eventos del listado

Si queremos que nuestro listado tenga un mínimo de funcionalidad, podemos utilizar los «Listeners» que incluye la clase «ListActivity», esto es, los métodos que se ejecutarán dependiendo del evento ocurrido. Con el siguiente código, que incluiremos en la actividad anterior, iniciaremos otra actividad a la que le pasaremos como información la «id» del elemento pulsado.

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
	super.onListItemClick(l, v, position, id);

	Intent intent = new Intent(this, DetalleEvento.class);
	intent.putExtra("id", Eventos.get(position).get("id"));
	startActivity(intent);
}

Como puede observarse, hacemos uso del Array Global «Eventos» que utilizábamos para rellenar la lista, en el cual almacenamos también la información del «id» sin mostrarla en el listado.

Código Fuente en GitHub

También te podría gustar...

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

28 Respuestas

  1. Akira dice:

    Que tal, tengo una pregunta: como puedo actualizar los datos capturados en android – sqlite hacia una base de datos SQLServer a través del wi-fi?, no se si con un socket y una especie de timestamp para que valide las actualizaciones, pero como lo implementaría en android?.

    Saludos.

  2. Luis dice:

    Estimado, disculpa las molestias, me gustaría preguntarte cómo puedo agregar ese listview en otra activity. Sucede que cuando coloco si quiera un elemento textview en la actividad row, no funciona. No aparecen datos ni lista. Me podrías ayudar? muchas gracias.

    • Miguel S. Mendoza dice:

      La peculiaridad de este proyecto es que utiliza un tipo de Actividad especial denominada ListActivity en la que se supone hay un objeto ListView en el fichero de interfaz. El definir el fichero row.xml es totalmente opcional ya que se pueden utilizar los predeterminados de android que se pueden encontrar en «android.R.layout». Para agregar todo esto en una actividad normal, es decir, «extends Activity» habrá que enlazar con el objeto ListView de forma manual:
      ListView lista = (ListView) findViewById(android.R.id.list);
      Será entonces a este objeto «lista» al que habrá que definir directamente los Listeners para que responda a los eventos, y el adapter para que rellene la lista con un fichero de interfaz para filas personalizado (row.xml) o no.
      Si añades objetos al fichero row.xml asegúrate de que esté todo bien definido, y que no se repitan las ids de los objetos, ya que a la hora de rellenar la lista dará error.
      Espero haber respondido a tu pregunta, 😉

  3. Akira dice:

    Que tal, tengo una problema, sucede que si presiono una elemento de la lista y llamo a otra actividad, al terminar, de regreso pierdo la posición, es decir, me regresa siempre al principio de la lista, como hacer para que me coloque en la posición en la que me quedé?

    Muchas gracias

    • Andres dice:

      oye no me funciono la action de la lista, puedes compartir tu codigo.
      por que estoy tratando de imprimir un toast con la posicion y no hace nada

      @Override
      protected void onListItemClick(ListView l, View v, int position, long id) {
      super.onListItemClick(l, v, position, id);
      Toast.makeText(getApplicationContext(), «Posicion » +» «+ id, Toast.LENGTH_SHORT).show();

      }

      • Andres dice:

        Solucionado, no sirve en version 2.2 lo probe en 4.0 y sirve al pelo.
        En 2.2 no entra al onListItemClick, por que? ni idea.

        • Miguel S. Mendoza dice:

          Probablemente en 2.2 ListActivity no funciona como debe. En estos casos siempre es recomendable hacerlo «a pelo»:

          ListView lista = (ListView) findViewById(android.R.id.list);
          lista.setOnItemClickListener(new OnItemClickListener() {

          @Override
          public void onItemClick(AdapterView adapter, View view,
          int position, long id) {
          // Hacer algo
          }
          });

          Así no hay fallo, tanto para onItemClickListener como para cualquier otro Listener, 😉

    • Miguel S. Mendoza dice:

      Tienes que guardar el estado justo antes de iniciar la segunda actividad. Para eso te creas una variable global de tipo Parcelable:

      Parcelable state;

      Y en ella almacenas el valor del estado de la lista antes de iniciar la nueva actividad:

      state = lista.onSaveInstanceState();

      Luego, para recuperar el estado, después de establecer el Adapter, lo restauras:

      lista.setAdapter(ListAdapter);
      lista.onRestoreInstanceState(state);

      Espero que te sirva, 😉

      • akira dice:

        Muchas gracias!!. Funciona!!. Soy nuevo en esto y no conocía las variables parcelables. Estaba intentando cosas marcianas.

        Mil gracias.

  4. Andres dice:

    La solucion era simple, el bendito manifest.

  5. Andres dice:

    NO me funciona, copie el codigo exacto lo unico es el main, lo tengo igual al comentario de arriba de Hrodas lo cambie como lo explicaste y tampoco hay te dejo el codigo, le agrradesco si me pueden colaborar que puede ser estoy haciendolo en ver 4.0 no se si eso afecte

    public class Pestana2 extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_pestana2);
    final Button btn = (Button)findViewById(R.id.button1);
    btn.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
    //Se crea el Intent
    Intent intent =new Intent(getApplicationContext(), Listado.class);
    //Iniciamos la nueva actividad
    startActivity(intent);
    }
    });

    }

    }

    • Andres dice:

      Me funciono si coloco el codigo de de Listado.java en el main, osea como si fuera la pagina principal, pero si la llamo desde otra no me funciona. Debe llamarse de alguna otra forma que no he encontrado, ayudenme por favor

  6. fernanda dice:

    Tengo este prgrama pero lo que pasa esk cuando quiero enlistar no me lo muestra al dar el boton de presionar me podrian ayudar??

    package com.example.proyecto;

    import android.os.Bundle;

    import android.app.Activity;
    import android.view.Menu;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.RadioButton;
    import android.widget.TextView;

    public class Proyecto extends Activity
    {

    EditText e1, e3, e2;
    TextView resultado;
    String texto, texto1,lista,aux1,aux2;
    Button c;
    RadioButton g,l,list;
    String nom[]= new String [10];
    String num[] = new String [10];
    Integer prom[] = new Integer [10];
    Integer i=0,aux3;

    @ Override
    protected void onCreate (Bundle savedInstanceState)
    {
    super.onCreate (savedInstanceState);
    setContentView (R.layout.activity_proyecto);

    resultado = (TextView) findViewById (R.id.t3);
    e1 = (EditText) findViewById (R.id.e1);
    e2 = (EditText) findViewById (R.id.e2);
    e3 = (EditText) findViewById (R.id.e3);
    g = (RadioButton) findViewById (R.id.radioButton1);
    l = (RadioButton) findViewById (R.id.radioButton2);
    list = (RadioButton) findViewById (R.id.radioButton3);
    c = (Button) findViewById (R.id.button1);

    }

    @ Override
    public boolean onCreateOptionsMenu (Menu menu){

    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater ().inflate (R.menu.activity_proyecto, menu);
    return true;
    }

    public void funcion (View v)
    {
    if(g.isChecked()==true){
    nom [i] = e1.getText ().toString ();
    num [i] = e2.getText ().toString ();
    prom [i]= Integer.parseInt(e3.getText().toString());
    i++;
    }
    }
    public void Presionar (View v){
    if(l.isChecked()==true){
    for(i=0;i<=10;i++){
    for(i=0;i<=10;i++){
    aux1=nom[i];
    nom[i]=nom[i+1];
    nom[i+1]=aux1;

    for(i=0;i<=10;i++){
    for(i=0;i<=10;i++){
    aux2=num[i];
    num[i]=num[i+1];
    num[i+1]=aux2;

    for(i=0;i<=10;i++){
    for(i=0;i<=10;i++){
    aux3=prom[i];
    prom[i]=prom[i+1];
    prom[i+1]=aux3;

    }
    }
    }
    }
    }
    }

    }

    resultado.setText (nom[i] + num[i] + prom[i]);
    }
    }

  7. daniela dice:

    cuantas clases tiene este programa?

  8. daniela dice:

    @Miguel S. Mendoza
    Muchas gracias miguel , vere si me resulta.
    gracias

  9. Si te fijas en el paso 4 del tutorial se hace precisamente eso. Se llama a una nueva actividad pasando como parametro una id:

    Intent intent = new Intent(this,DetalleEvento.class);
    intent.putExtra(«id»,Eventos.get(position).get(«id»));
    startActivity(intent);

    Desde la segunda actividad (DetalleEvento en este caso), la que tiene que mostrar los datos, tendrás que obtener esos datos introducidos en el intent con el siguiente código:

    Bundle extras = getIntent().getExtras();
    String id;

    if (extras != null) {
    id = extras.getString(«id»);
    }

    Si guardas los datos de los pedidos en una base de datos por ejemplo, con pasarle la ‘id’ a la actividad que tiene que mostrar la información es suficiente, porque puedes usar ese dato para obtener el resto de la información de la base de datos. Hay otro tutorial en esta web sobre bases de datos en android que podrías utilizar. En el incluyo un proyecto bastante decente para descargar al final del todo: http://blog.netrunners.es/usar-nuestra-propia-base-de-datos-sqlite-en-android/

    De todos modos, si no quieres utilizar una base de datos, y toda la información que necesitas está en la Actividad que muestra la lista y llama a la segunda actividad, puedes meter toda la información que necesites en el intent, como se mete la ‘id’ en el tutorial, y obtenerla y mostrarla en la segunda actividad.

  10. daniela dice:

    Hola tengo una consulta haber si me pueden ayudar :

    Tengo una lista con ejemplo numeros de pedidos , lo que quiero es que cuando se seleccione un numero de pedido. Se abra otra ventana con toda la informacion del pedido :(lugar , direccion, boleta , etc).

    Como lo puedo hacer ??

  11. Cambia:

    startActivity(new Intent (ActivityActivity.this, SecondActivity.class));

    Por:

    startActivity(new Intent (getApplicationContext(), SecondActivity.class));

    Si sigue dándote problemas pásame el código del layout «main.xml» dónde está el botón por si hubiera algo ahi que no debería estar.

  12. HRodas dice:

    Ejemplo simple con errores: no se porque.

    import andoid.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;

    public class ActivityActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Button btn = (Button) findViewById (R.id.btnactivity);
    btn.setOnClickListener(new OnClickListener() {

    @Override
    public void onClick(View v)
    {
    startActivity(new Intent (ActivityActivity.this, SecondActivity.class));
    }

    });

    }
    }

  13. Lleváis toda la razón del mundo. Os pido disculpas por las posibles molestias ocasionadas. La solución es bien simple, y la he modificado ya en el post:
    Cambiar «ListAdapter» por «SimpleAdapter».
    Si veis cualquier otra cosa no dudéis en consultarlo. Procuraré contestar con mayor rapidez a partir de ahora.

  14. Juan dice:

    El problema es que ListAdapter es una interfaz y no se pueden instanciar interfaces. Ese código no puede haberte funcionado nunca. Hay que cambiar eso por otro ListAdapter

  15. SavidSalazar dice:

    Tengo el mismo error y si cuento con los imports que pides

    package com.fastech.listado;

    import java.util.ArrayList;
    import java.util.HashMap;

    import android.app.ListActivity;
    import android.os.Bundle;
    import android.widget.ListAdapter;
    public class Listado extends ListActivity {

    //Mantenemos un Array de elementos en la que podremos guardar
    //más información de la que mostraremos en el listado
    ArrayList<HashMap> Eventos;

    //Con los siguientes Arrays establecemos la correspondencia
    //entre los elementos del Array de HashMaps de eventos (from)
    //con los elementos del diseño en XML de cada una de las filas (to)
    String[] from=new String[] {«Time»,»Name»,»Desc»};
    int[] to=new int[]{R.id.hora,R.id.nombre,R.id.desc1};

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // Este método de obtención de elementos puede cambiarse por cualquier otro
    //como leerlos de una BBDD o de un servidor web con JSON
    ArrayList lista = new ArrayList();

    String[] evento1 = {«11:30″,»Ofrenda de Flores»,»Participa la Banda Sinfónica Municipal de Albacete», «1»};
    lista.add(evento1);

    String[] evento2 = {«12:00″,»Los Redondeles»,»La ronda de los Redondeles 2011″, «2»};
    lista.add(evento2);

    String[] evento3 = {«12:00″,»Futbol»,»Albacete Balompie Vs. Tenerife C.D.», «3»};
    lista.add(evento3);

    // Transformamos los elementos String[] en HashMap para
    //posteriormente incluirlos en el Array Global que se utilizará
    //para rellenar la lista
    Eventos = new ArrayList<HashMap>();
    for(String[] evento:lista){
    HashMap datosEvento=new HashMap();

    // Aquí es dónde utilizamos las referencias creadas inicialmente
    //en el elemento «from»
    datosEvento.put(«Time», evento[0]);
    datosEvento.put(«Name», evento[1]);
    datosEvento.put(«Desc», evento[2]);
    datosEvento.put(«id», evento[3]);

    Eventos.add(datosEvento);
    }
    // Una vez tenemos toda la información necesaria para rellenar la lista
    //creamos un elemento que nos facilitará la tarea:
    //ListAdapter(Actividad, Array de HashMap con elementos, Fichero XML del
    //diseño de cada fila, Cadenas del HashMap, Ids del Fichero XML del diseño de cada fila)
    ListAdapter ListadoAdapter=new ListAdapter(this, Eventos, R.layout.row, from, to);
    setListAdapter(ListadoAdapter);
    }
    }

    Si me pudieras saber te lo agradeceria.
    Saludos y Gracias por el tutorial

  16. luis dice:

    efectivamente estoy trabajando con eclipse, recien aprendiendo y la verdad no he podido encontrar la forma de solucionarlo. si puedes serias de mucha ayuda :c

  17. Miguel dice:

    En el tutorial no se incluyen los «imports» correspondientes a cada una de las clases. Los problemas de instanciacion vienen por la falta de los mismos. Si utilizais eclipse haciendo clic en el icono del error te ofrece la posibilidad de añadir los distintos imports de las clases que faltan.
    De todas formas, para más información sobre la clase ListAdapter podeis echarle un ojo da la siguiente documentación: http://developer.android.com/reference/android/widget/ListAdapter.html

  18. luis dice:

    tambien tengo problemas con Cannot instantiate the type ListAdapter
    estoy ocupando la version 4.0 de android. y he hecho todo lo que sale tal cual :c

  19. Miguel dice:

    Add this:

    import android.widget.ListAdapter;

  20. Santos dice:

    Erro this line:
    ListAdapter ListadoAdapter = new ListAdapter(this, Eventos, R.layout.row, from,to);

    description of error
    Cannot instantiate the type ListAdapter

    • Rafael dice:

      Que tal Santos, el error que te manda es por que no te este reconoce a que actividad te estas refiriendo, no se como esccribirlo pero poniendo

      ListAdapter listado = new ListAdapter(ActivityName.this, Eventos, etc…);

      es decir cambias «this» por «nombreActivity.this» y ya no te mande ese error.
      Saludos