Programación Android, ListView & Adapter personalizado I

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0

Ahora que hemos empezado a manejar adaptadores y listas con el Spinner, vamos a dar un paso más, utilizando el control ListView y gestionando su contenido con un Adapter personalizado, de manera que lo que visualicemos en cada celda de la lista esté diseñado por nosotros mismos.

Creando el ListView

En primer lugar debemos crear nuestro interfaz de usuario. Lógicamente debemos añadir un ListView, en el que mostraremos los datos que queramos, y en nuestro caso vamos a incorporar en la parte superior algunos TextView que indicarán el nª de la celda que ocupa el ítem seleccionado del ListView y el texto que contenga.

Quedaría así:

Layout ListView
Layout ListView

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".ExampleListViewMain" >

    <TextView
        android:id="@+id/tvItemField"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/tvItemField" />

    <TextView
        android:id="@+id/tvItemContent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/tvItemField"
        android:text="@string/tvItemComponent" />

    <ListView
        android:id="@+id/lvItems"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/tvItemContent" >
    </ListView>

</RelativeLayout>

La forma en que asignaremos el Adapter a nuestro ListView es exactamente igual que como lo hicimos en el ejemplo del Spinner, sin embargo en esta ocasión dijimos que usaríamos uno personalizado, por lo que por un lado tendremos que crear una vista de cada ítem del ListView, que será la base que utilicemos para poder mostrar los datos, y por otro nuestra propia clase que actúe como Adapter.

Creando la vista del ítem del ListView

Para este ejemplo voy a continuar con la idea que desarrollamos en el Spinner, usando animales para mostrar en el ListView, y mostraremos la foto del animal en cuestión, el nombre y el número de celda que está ocupando en el ListView. En primer lugar vamos a crear un nuevo layout para el ítem del ListView, quedaría así:

Ítem del ListView
Ítem del ListView
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin" >

    <ImageView
        android:id="@+id/imgAnimal"
        android:layout_width="75dp"
        android:layout_height="75dp"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:src="@android:drawable/ic_dialog_info" />

    <TextView
        android:id="@+id/tvField"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_toRightOf="@+id/imgAnimal"
        android:text="TextView" />

    <TextView
        android:id="@+id/tvContent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/imgAnimal"
        android:layout_toRightOf="@+id/imgAnimal"
        android:text="TextView" />

</RelativeLayout>

Creando la estructura de datos

Y a continuación crearemos en Java, en la Activity Main, la colección de animales que querremos mostrar en el ListView. En esta ocasión utilizaremos algunos animales más, para poder llenar el ListView con más celdas, y para poder mostrar las imágenes tendremos que incorporarlas a los recursos de nuestro proyecto, creando una carpeta “drawables” en la que las guardaremos. Para que resulte más sencillo podrán descargarse las imágenes usadas en este ejemplo aquí.

Al igual que en el ejemplo del Spinner, vamos a utilizar un string-array que definiremos como recursos en el xml, pero en lugar de utilizar un TreeMap como hicimos en el Spinner, en esta ocasión vamos a crear una clase modelo que nos sirva para guardar nombre y foto del animal, y este objeto lo almacenaremos en un ArrayList. El motivo por el que en esta ocasión utilizamos este método es que el Adapter necesita una colección que implemente la interfaz List, cosa que no sucede con el TreeMap y sí con el ArrayList.

En primer lugar creamos la clase modelo, llamada Animal, y que contendrá un campo String en el que poner el nombre y otro int en el que poner la imagen. Utilizamos un int ya que al estar las imágenes almacenadas en la carpeta drawables podremos acceder al recurso a través de su id (int), de manera que no necesitaremos almacenar un Bitmap, lo que necesitaría más memoria y por lo tanto haría innecesariamente más pesada nuestra aplicación, y además veremos posteriormente que podemos asignar la imagen al ImageView directamente con este valor, utilizando uno de sus métodos.

La clase Animal quedaría así:

public class Animal {
	private String nombre;
	private int drawableImageID;

	public Animal(String nombre, int drawableImageID) {
		this.nombre = nombre;
		this.drawableImageID = drawableImageID;
	}

	public String getNombre() {
		return nombre;
	}

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}

	public int getDrawableImageID() {
		return drawableImageID;
	}

	public void setDrawableImageID(int drawableImageID) {
		this.drawableImageID = drawableImageID;
	}

}

Ahora tendremos que declarar en nuestra Activity un ArrayList:

private ArrayList animales;

Esta variable la inicializaremos en el método onCreate de nuestra Activity, y rellenaremos los datos utilizando un método, para que nuestro onCreate no quede sobrecargado de datos. El método quedaría así:

private void rellenarArrayList() {
	animales.add(new Animal("aguila", R.drawable.aguila));
	animales.add(new Animal("ballena", R.drawable.ballena));
	animales.add(new Animal("caballo", R.drawable.caballo));
	animales.add(new Animal("camaleon", R.drawable.camaleon));
	animales.add(new Animal("canario", R.drawable.canario));
	animales.add(new Animal("cerdo", R.drawable.cerdo));
	animales.add(new Animal("delfin", R.drawable.delfin));
	animales.add(new Animal("gato", R.drawable.gato));
	animales.add(new Animal("iguana", R.drawable.iguana));
	animales.add(new Animal("lince", R.drawable.lince));
	animales.add(new Animal("lobo", R.drawable.lobo_9));
	animales.add(new Animal("morena", R.drawable.morena));
	animales.add(new Animal("orca", R.drawable.orca));
	animales.add(new Animal("perro", R.drawable.perro));
	animales.add(new Animal("vaca", R.drawable.vaca));
}

Creando el Adapter

Una vez que ya tenemos la colección de datos creada, ya podemos empezar a crear nuestro Adapter. El Adapter que vamos a utilizar es una nueva clase que crearemos, pero que va a extender de la clase ArrayAdapter, que ya implementa los métodos que nuestra clase necesita, y sólo tendremos que sobreescribir uno de ellos. La firma de esta clase sería:

public class AnimalesAdapter extends ArrayAdapter

Como hemos dicho, esta clase extiende de ArrayAdapter, pero también se puede ver que hemos parametrizado el ArrayAdapter con la clase modelo que nosotros hemos creado. En realidad esto no es necesario, pero sí es conveniente, ya que luego vamos a manejar una colección de datos, y al parametrizar en este punto haremos que la colección que se utilice posteriormente obligatoriamente sea del tipo aquí indicado. Es decir, si por accidente intentamos decir que utilice un ArrayList nos dará fallo, porque no estamos preparando nuestra clase para ese tipo de colección.

En esta clase necesitaremos crear dos variables, una de tipo Context, que será la que utilicemos para “inflar” las vistas que mostraremos en cada celda del ListView, y un ArrayList en el que guardar la colección de datos:

private Context context;
private ArrayList datos;

Estas variables las inicializaremos en el constructor:

public AnimalesAdapter(Context context, ArrayList datos) {
	super(context, R.layout.listview_item, datos);
	// Guardamos los parámetros en variables de clase.
	this.context = context;
	this.datos = datos;
}

Como podemos ver en el constructor estamos haciendo una llamada a super(). Recordemos que es la llamada al constructor de la clase que está por encima de la nuestra (de la que hemos extendido, que en este caso es ArrayAdapter). ArrayAdapter tiene varios constructores distintos, sin embargo para nuestros propósitos el que hemos utilizado es el que mejor nos viene. Como podemos ver hemos pasado a super el Context, la colección de datos y además hemos pasado la referencia al layout que creamos para la vista de cada celda del ListView.

Una vez que tenemos creado ya el constructor e inicializadas las variables pasamos al método realmente importante de esta clase. El método getView() es el que realmente se encarga de inflar y mostrar las vistas de cada celda del ListView. Éste es un método que está ya implementado en la clase ArrayAdapter, pero que nos obliga a sobreescribir en nuestra clase:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
	// En primer lugar "inflamos" una nueva vista, que será la que se
	// mostrará en la celda del ListView. Para ello primero creamos el
	// inflater, y después inflamos la vista.
	LayoutInflater inflater = LayoutInflater.from(context);
	View item = inflater.inflate(R.layout.listview_item, null);

	// A partir de la vista, recogeremos los controles que contiene para
	// poder manipularlos.
	// Recogemos el ImageView y le asignamos una foto.
	ImageView imagen = (ImageView) item.findViewById(R.id.imgAnimal);
	imagen.setImageResource(datos.get(position).getDrawableImageID());

	// Recogemos el TextView para mostrar el nombre y establecemos el
	// nombre.
	TextView nombre = (TextView) item.findViewById(R.id.tvContent);
	nombre.setText(datos.get(position).getNombre());

	// Recogemos el TextView para mostrar el número de celda y lo
	// establecemos.
	TextView numCelda = (TextView) item.findViewById(R.id.tvField);
	numCelda.setText(String.valueOf(position));

	// Devolvemos la vista para que se muestre en el ListView.
	return item;
}

Como hemos visto en el código, lo primero que debemos hacer es inflar la vista, y para ello utilizamos un objeto de la clase LayoutInflater y para inicializarlo hemos necesitado el context que nos vino desde el constructor. Es especialmente importante que el context que recibamos sea realmente el de la Activity que maneja el ListView, ya que si no es así al ejecutar la aplicación nuestro ListView quedará como apagado, y no permitirá que se visualice del todo bien.

Después hemos creado un objeto de la clase View, y lo hemos inicializado a partir de nuestro inflater. El inflater ha utilizado dos parámetros, el primero apunta a la referencia al Layout que creamos para el ListView, y el segundo hace referencia a la vista que será “padre” de la nuestra. Como en este caso no hay ningún Layout que vaya a contener a este, sino que directamente lo vamos a introducir en el ListView, pasamos null como segundo parámetro.

Una vez creada nuestra vista empezaremos a asignar los valores a los TextView y al ImageView. Para hacerlo vemos que es muy similar a como lo hemos hecho en las Activity que hemos configurado hasta ahora, pero hay que prestar especial atención a como inicializamos las variables:

ImageView imagen = (ImageView) item.findViewById(R.id.imgAnimal);
TextView nombre = (TextView) item.findViewById(R.id.tvContent);
TextView numCelda = (TextView) item.findViewById(R.id.tvField);

Podemos ver que en esta ocasión en lugar de poner el método findViewById() directamente lo hemos hecho a partir del objeto View que creamos. Esto es así en primer lugar porque este método no se encuentra en la clase ArrayAdapter, y en segundo lugar porque los TextView y el ImageView a los que queremos acceder realmente se encuentran dentro de esa vista (en el layout que hemos utilizado para crear la vista), por lo que obligatoriamente tenemos que partir de este objeto para alcanzar los TextView y el ImageView.

Después sólo hemos tenido que darle valores al texto de los TextView, y la imagen al ImageView. Y como dijimos antes, para asignar la imagen hemos utilizado el método setImageResource(), que pide como parámetro la referencia al objeto que queremos utilizar, es decir la ID que almacenamos en nuestra clase Animal.

Otro dato a tener en cuenta es cómo hemos creado las vistas para todas las celdas, como podemos ver en la firma del método getView() uno de los parámetros que recibe es position, que se refiere al número de celda que estamos inflando, que siempre coincidirá con el índice de nuestra colección, de manera que en la celda 0 tendremos el animal que ocupe la posición 0 del ArrayList.

Una vez que tenemos creado el Adapter, en la Activity del ListView sólo tenemos que asignárselo al ListView, para ello declaramos una variable de la clase AnimalesAdapter que acabamos de crear, la inicializaremos utilizando su constructor y por último se lo asignaremos al ListView:

AnimalesAdapter adapter;
// Inicializamos el adapter.
adapter = new AnimalesAdapter(this, animales);
// Asignamos el Adapter al ListView, en este punto hacemos que el
// ListView muestre los datos que queremos.
lvAnimales.setAdapter(adapter);

Implementando el Listener

Por último sólo nos falta establecer la lógica que hará que cambien los TextView de nuestra Activity al pulsar un ítem del ListView. Para ello primero vamos a hacer que nuestra Activity implemente el interfaz OnItemClickListener:

public class Main extends Activity implements OnItemClickListener

Y esto nos va a obligar a sobreescribir el método onItemClick():

@Override
public void onItemClick(AdapterView<?> adapter, View view, int position,
		long ID) {
	// Al hacer click sobre uno de los items del ListView mostramos los
	// datos en los TextView.
	tvNombre.setText(animales.get(position).getNombre());
	tvNumCelda.setText(String.valueOf(position));

}

Como parámetro del método onItemClick() recibimos también la posición, por lo que sólo tendremos que recuperar el animal que se encuentra en esa posición en el ArrayList animales para acceder al nombre del animal, y utilizar esta posición para mostrarla en el TextView. Una vez implementada la interfaz, sólo tendremos que asignarle el Listener al ListView, para que se ejecute cuando seleccionemos un ítem:

lvAnimales.setOnItemClickListener(this);

Con esto ya estamos manejando un ListView con vistas, sin embargo esta forma de implementar el Adapter tiene un grave problema de optimización, ya que si las colecciones que utilizamos son excesivamente grandes estamos almacenando demasiadas vistas en memoria. En la próxima entrada explicaremos cómo optimizar el Adapter para que consuma la menor cantidad de memoria posible.

Descargas

Puedes ver el resultado del ejemplo en tu móvil descargando nuestra aplicación desde Google Play.

Puedes acceder a nuestro repositorio para bajar el proyecto aquí.

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0
The following two tabs change content below.
Reborn as IT Developer. Desarrollador Android y fundador de Proyecto Simio. "En realidad, yo no puedo enseñar nada a nadie, sólo puedo hacerles pensar." - Sócrates.

33 thoughts on “Programación Android, ListView & Adapter personalizado I

  1. Estimado Simio, el proyecto para descargarse ya no está disponible. Si es tan amable de subirlo, me ayudaría muchísimo, ya que no puedo activar el ‘setOnItemSelectedListener’ y quiero ver en que fallo.

  2. Excelente articulo. Estoy empesando con el RecyclerView y ListView. Estuve agregando mas componente como un EditText para poner una cantidad, resulta que tengo 50 item y al agregar un numero en el primer item al hacer un scroll para dirigirme a otro articulo el valor que puse al primer item se pasa a otro. quiere saber como mitigar ese problema. Gracias

  3. Muchísimas gracias por tu aporte!
    Tengo una duda, si yo quisiera que al pinchar en un animal, este se borrase y se actualizara la lista … ¿cuál sería la forma?
    He pensado en el onItemClick meter un animales.remove(position) pero no sé como actualizar el listView

    Muchas gracias

  4. Buenas tardes y muchas gracias por todo esto, quería preguntarles si a alguien le sucede que la ultima vista es la que “infla” a todo el listview?. estoy luchando con este error, se me van agregando los items, pero son todos iguales a la ultima fila que agrego al arrayList .¡?? les dejo parte de codigo porque completo es muucho->
    //mi adapter
    public class item_adaptador extends ArrayAdapter {
    private Context context;
    private ArrayList datos;
    public item_adaptador(Context context, ArrayList datos) {
    super(context, R.layout.item, datos);
    this.context = context;
    this.datos = datos;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

    LayoutInflater inflater = LayoutInflater.from(context);
    View itemV = inflater.inflate(R.layout.item, null);
    TextView codigo_f = (TextView) itemV.findViewById(R.id.codigo_f);
    codigo_f.setText(String.valueOf(datos.get(position).get_id_Sistema()));
    TextView nombre_f = (TextView) itemV.findViewById(R.id.nombre_f);
    nombre_f.setText(datos.get(position).get_Nombre());
    TextView punit_f = (TextView) itemV.findViewById(R.id.punit_f);
    float precio_unitario = datos.get(position).get_precio();
    punit_f.setText(String.valueOf(precio_unitario));
    TextView cantidad_f = (TextView) itemV.findViewById(R.id.cantidad_f);
    int cantidad = datos.get(position).get_cantidad();
    cantidad_f.setText(String.valueOf(cantidad));

    TextView total_f = (TextView) itemV.findViewById(R.id.total_f);
    total_f.setText(String.valueOf(cantidad * precio_unitario));
    return itemV;
    }
    }
    //mi metodo para agregar items al listview

    public void sumar_item(View view) {

    int pos = mListV.getSelectedItemPosition();
    if (pos != AdapterView.INVALID_POSITION && pos != -1) {

    Toast.makeText(this, ((Cursor) mListV.getSelectedItem()).getString(2), Toast.LENGTH_SHORT).show();

    EditText np = (EditText) findViewById(R.id.numberPicker_cantidad);
    int cant;

    if (np.getText().toString().matches(“”)) {
    cant = 1;
    } else {
    cant = Integer.parseInt(np.getText().toString());
    }

    item newUser = new item(((Cursor) mListV.getSelectedItem()).getInt(0),
    ((Cursor) mListV.getSelectedItem()).getInt(1),
    ((Cursor) mListV.getSelectedItem()).getString(2),
    ((Cursor) mListV.getSelectedItem()).getFloat(4), cant);
    // adapter.add(newUser);
    arreglodeitems.add(newUser);

    adapter.notifyDataSetChanged();

    1. No he podido pararme a mirar exactamente el código, pero por lo que cuentas parece que se están cruzando las referencias de los objetos que usas y al final todas las filas están utilizando el mismo objeto, por lo que al modificarlo para la última fila también se está modificando para el resto. Tendrás que hacer debug de tu código para comprobar esto.

      De todas formas te recomiendo que sigas con el resto de ejemplos, ya que más adelante explico cómo mejorar el rendimiento de los listados. Posiblemente al aplicar estas técnicas se resuelva tu problema.

      Un saludo :)

  5. Bueans no doy con la clave consegui hacer un listView con imagen, y cuando des click que te devuelva el nombre pero ahora no soy capaz de hacer que valla a otra actividad llamada usuario. HAber si me podeis ayudar con el codigo. Gracias.

    protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_actividad2); textView = (TextView)findViewById(R.id.texto);

    PrincipalMenu principalmenu_datos[] = new PrincipalMenu[]{

    new PrincipalMenu(R.drawable.informacion,”Menu de Usuario”),
    new PrincipalMenu(R.drawable.logistica,”Area de Logistica”),
    new PrincipalMenu(R.drawable.cadena,”Area de Produccion”),
    new PrincipalMenu(R.drawable.calidad,”Area de Calidad”),
    new PrincipalMenu(R.drawable.mantenimiento,”Area de Mantenimiento”),
    new PrincipalMenu(R.drawable.trabajo,”Area de Ingenieria”),
    new PrincipalMenu(R.drawable.pegaso,”Area de Laboratorio”),
    new PrincipalMenu(R.drawable.personal,”Area de Personal”),
    new PrincipalMenu(R.drawable.ok,”Area de Utilidades”),
    new PrincipalMenu(R.drawable.manuales,”Area de Manuales”),

    };
    PrincipalMenuAdapter adapter = new PrincipalMenuAdapter(this,R.layout.listview_item_row,principalmenu_datos);

    listView = (ListView) findViewById(R.id.listview);

    View header = (View) getLayoutInflater().inflate(R.layout.list_header_row,null);
    listView.addHeaderView(header);
    listView.setAdapter(adapter);
    listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
    @Override
    public void onItemClick(AdapterView parent, View view, int position, long id){
    TextView v = (TextView)view.findViewById(R.id.tv);

    Toast.makeText(getApplicationContext(),v.getText(),Toast.LENGTH_SHORT).show();

    }
    });

  6. Hola Igor,

    Te felicito por el blog lo primero. Para los que empezamos es de mucha ayuda. Aquí va mi duda: estoy intentando implementar un listview personalizado con un textview y un checkbox. Mi problema es que no soy capaz de poblar el listview con un array definido en el fichero Strings.xml . El adapter que tengo ahora mismo es este:
    class RespuestaAdapter extends ArrayAdapter{

    private List respuestaList;
    private Context context;

    public RespuestaAdapter(List respuestaList, Context context){
    super(context, R.layout.single_listview_item, respuestaList);
    this.respuestaList = respuestaList;
    this.context = context;
    }

    private static class RespuestaHolder{
    public TextView textViewRespuesta;
    public CheckBox checkBoxRespuesta;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

    View view = convertView;

    RespuestaHolder respuestaHolder = new RespuestaHolder();

    if(convertView == null){

    LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    view = layoutInflater.inflate(R.layout.single_listview_item, null);

    respuestaHolder.textViewRespuesta = (TextView) view.findViewById(R.id.textViewRespuesta);
    respuestaHolder.checkBoxRespuesta = (CheckBox) view.findViewById(R.id.checkBoxRespuesta);

    }else{
    respuestaHolder = (RespuestaHolder) view.getTag();
    }

    Respuesta respuesta = respuestaList.get(position);
    respuestaHolder.textViewRespuesta.setText(respuesta.getRespuesta());
    respuestaHolder.checkBoxRespuesta.setChecked(respuesta.isChecked());
    respuestaHolder.checkBoxRespuesta.setTag(respuesta);

    return view;
    }

    }

    De momento los datos los introduzco en el Main.java de esta manera:
    respuestaList.add(new Respuesta(“Alerta”));

    Y me gustaría poder hacerlo con un string-array definido en Strings.xml . Sé que la definición del array sería algo así: String [ ] miArray = getResources().getStringArray(R.array.miArray_array);

    Pero a partir de aquí no sé como modificar mi adapter para que funcione.

    Muchas gracias por adelantado y un saludo

    1. Buenas Jose Alberto,

      En primer lugar gracias por el comentario, y disculpa que haya tardado tanto en responderte, estos días he estado bastante ocupado y no he podido parar antes a hacerlo.

      Sobre tu duda te comento, lo primero que veo es que deberías parametrizar tu ArrayAdapter, para que sea específico para un tipo de objeto:

      class RespuestaAdapter extends ArrayAdapter<Respuesta> {
      // ...
      }
      

      En segundo lugar, para optimizar tu adaptador, sé que en mi ejemplo se utiliza un objeto en la clase para almacenar la colección del adaptador, sin embargo hay una forma más eficiente de hacerlo, permitiendo que el propio ArrayAdapter (la clase de la que heredas) almacene esta colección (que de hecho ya lo hace al pasárselo al super), y así evitar tener en memoria otra colección más, y cuando tengas que hacer uso de un elemento de la colección utilizas “getItem(position)” en lugar de usar el objeto que guardas en tu clase (y que por lo tanto ya puedes borrar):

      Respuesta respuesta = getItem(position);
      

      Por último creo que tu problema está en el punto en el que tienes que pasar de tu colección de String a una colección de Respuesta para poder pasárselo al adaptador, para eso tendrías que recorrer tu colección de String para crear a partir de ella otra colección de Respuesta, utilizando un for o un for-each:

      ArrayList<Respuesta> respuestas = new ArrayList<Respuesta>();
      for(String tmp : miArray) {
          respuestas.add(new  Respuesta(tmp, <Resto de parámetros necesarios>));
      }
      // Inicializar el adaptador con la colección de Respuesta.
      

      Con esto en principio debería funcionarte, sólo tener en cuenta que si el resto de parámetros que puedan ser necesarios siempre tienen el mismo valor te recomiendo inicializar con un único parámetro y en el constructor dar el valor por defecto al resto de parámetros.

      Espero haber resuelto tu duda :)
      Un saludo.

  7. Hola Igor,

    Lo primero darte las gracias por toda esta cantidad de información. Acabo de empezar en esto de la programación y resulta de muchísima ayuda. Tengo una duda con esto de los listviews. En lugar de un ArrayList que se carga en el main, me gustaría recoger la información de un string-array que tengo definido en el fichero strings.xml. Muchas gracias por adelantado y un saludo.

    1. Tanto para los Spinners como para los ListViews tienes la opción de utilizar en XML la propiedad android:entries=”@array/id_del_string-array”. Con esto te evitas incluso tener que crear el adapter. Si en algún momento necesitas acceder al adapter del ListView puedes hacerlo con ArrayAdapter adapter = (ArrayAdapter)listView.getAdapter();

      Un saludo y gracias a ti por los comentarios :)

  8. Tengo en mi aplicacion un TabHost con varias pestañas, en dos de ellas muestro un listView en cada una ¿Como habria de implementar el ItemClickListener en la misma activity para cada uno de ellos? , porque en el ejemplo de arriba se utiliza solo uno.

    1. Buenas Fernando,

      En el caso de que estés utilizando Fragments, el OnItemClickListener debes implementarlo en el Fragment que contiene el ListView, y no en la Activity. En el método onCreateView del fragment inicializas tu objeto ListView y posteriormente le estableces el Listener igual que si fuera una Activity, pero se hace en el Fragment.

      Lo único que sí tendrás que recoger en la Activity son los eventos onClick que designes desde XML, es decir, si en tu XML pones un botón y le añades la propiedad onClick=”nombreMetodo” ese “nombreMetodo” tendrás que crearlo en tu Activity, por lo demás todas las referencias y todos los listeners, adapters, etc., tendrás que manejarlos dentro del Fragment.

      Un saludo.

  9. Buenas!!, el tuto esta genial, pero me preguntaba como adaptarlo a mi aplicacion.
    Yo almaceno datos de ubicaciones en una base de datos con SQlite, antes utilizaba un simpleCursor Adapter para mostrar los datos de mis listViews y todo funcionaba correctamente.
    Ahora estoy intentando aplicar typos de fuentes diferentes, incluidas a los items del los ListViews. Por lo que he leido en internet se puede hacer mas facil utilizando un adaptador personalizado, y he ahi mi duda:

    * Con el SimpleCursorAdapter utilizaba este codigo:

    String[] from = new String[]{bdl.ID_NOMBRE, bdl.ID_LATITUD, bdl.ID_LONGITUD, bdl.ID_FILA};
    int[] to = new int[]{R.id.tvNombre, R.id.tvLatitud, R.id.tvLongitud, R.id.tvIdentif};

    cursorLocDeEmergEditar = bdl.cargarCursorLocalizacion();
    miAdapterLocDeEmergEditar = new SimpleCursorAdapter(getApplicationContext(), R.layout.item_lista_loc, cursorLocDeEmergEditar, from, to, 0);

    lvEditar.setAdapter(miAdapterLocDeEmergEditar);

    * ¿Como podria cargar los datos para mostrarlos en el adaptador que tu propones?

    1. Pues podrías hacer exactamente lo mismo que he hecho yo, creando un adaptador que extienda de ArrayAdapter, o crear otro que extienda de CursorAdapter.

      Un saludo.

  10. una preguntita… existira una manera de sumar los campos que muestre un listview? yo muestro a traves de datos obtenidos en una base de datos un campo de “montos” quisiera mostrar la suma de estos campos en un textbox… me ayudan?

    1. Buenas Mónica

      Con un bucle que recorra todos los elementos de la colección que utilizas para llenar el listview puedes sumar el total que necesites.

      Un saludo.

  11. Hola, muy buen tutorial, solo quería hacerte una pregunta: ¿Cómo puedo implementar un ListView en Tabs (Sliding Tabs para ser más precisos) es que ando haciendo una aplicación que mostrará información en un ListView pero no logro primero ponerle esté último a mi Sliding Tab. Saludos

    1. Buenas Jonathan. Para conseguir esto tendrías que hacer lo siguiente:
      – Crear un xml con la vista que quieres definir (layout con listview y demás controles que quieras incorporar).
      – Crear una clase que extienda de Fragment, que en lugar de contener el TextView como en este ejemplo contendrá el ListView y demás controles que añadas.
      – Por último, en el método onCreateView inicializas las referencias a todos los controles que tengas en el layout y quieras manipular, además del Adapter que quieras utilizar para rellenar el ListView.

      Lo único a tener en cuenta es que si al fragment le tienes que pasar un modelo que has creado tú mismo (una clase que hayas creado tú), tendrías que hacer que esa clase implementara la interfaz Serializable o Parcelable, y se añadiría y extraería del Bundle con los métodos putSerializable()/getSerializable() o putParcelable()/getParcelable(), haciendo el correspondiente casting a tu clase:

      MiClase obj = (MiClase)getSerializable(KEY);
      

      Si no estás acostumbrado a implementar estas interfaces, por rapidez y sencillez te recomiendo Serializable, pero en eficiencia es superior implementar Parcelable.

      Un saludo.

  12. Buen día, agradezco que comparta su conocimiento con todos nosotros cibernautas.
    Estoy interesado en el manejo de los listview y me pregunto ¿Cómo será posible que al evento OnClickListener de cada item abra la información en otro layout o adaptador, no se como funcione realmente ese tipo de ejercicio?

    Por ejemplo:

    Item 1 -> clic –> layout 1 con información
    item 2 -> clic –> layout 2 con información
    .
    .
    .

    Espero recibir alguna respuesta al respecto.

    Saludos!

    1. En el Listener OnItemClickListener de un ListView tienes acceso a la posición del item sobre el que se ha hecho click, de esta manera puedes acceder al item y tomar las decisiones que necesites en tu código en función del elemento que contenga:

      public void onItemClick(AdapterView&lt;?&gt; adapter, View v, int position,
      		long id) {
      	Item item = lvAdapter.getItem(position);
      	// Evaluar item y tomar decisiones.
      }
      

      Espero haber podido aclararte la duda :)

      Un saludo.

  13. hola amigo muy ineteresando tu post pero quisera llenar los datos del listview con datos de BD mysql …tendras alguna idea o ejemplo como hacerlo

  14. Una preguntota! segui el tuto asi tal cual, y me sale perfecto! :) (enseñas muy bien). Pero eh intentando implentar este tipo de listas para poder varias de estas listas en una aplicacion, ya sea con un menu, o tab, o lo que sea! y no puedo!!! :S Podrias ayudarme?

    1. Buenas Eduardo, muchas gracias por seguirnos :)

      Así sin información es imposible que pueda saber dónde está tu problema, pero si lo que intentas es que el menú sea un listado como este, el funcionamiento del menú es muy distinto al de este tipo de listados, y lo mismo pasa con los tabs, cada cosa tiene su funcionamiento.

      En http://www.proyectosimio.com/indice-de-contenidos encontrarás tutoriales sobre cómo crear y configurar menús.

      Un saludo.

  15. Ya logre dar con la solución, muchas gracias de todos modos, aplicando las correcciones que sugeriste,así mismo heredando desde la clase BaseAdapter.

  16. Saludos Igor, estoy siguiendo tu tutorial, realizando algunas adecuaciones, y me encuentro con este problema, que al momento de cargar los datos al listview se genera una excepcion “println needs a message”

    Anexo mi codigo , esperando me puedas apoyar a encontrar la solucion

    public class Actividad {

    private String actividad,contacto,hora;
    private int id_agenda;

    public Actividad(String actividad, String contacto, String hora, int id_agenda)
    {
    this.actividad = actividad;
    this.contacto = contacto;
    this.hora = hora;
    this.id_agenda = id_agenda;
    }

    //geters
    public String getActividad()
    {
    return this.actividad;
    }

    public String getContacto()
    {
    return this.contacto;
    }

    public String getHora()
    {
    return this.hora;
    }

    public int getId_agenda()
    {
    return this.id_agenda;
    }
    }

    package gc.administracion.agendagc;

    +imports

    /**
    * Created by Valle on 14/02/14.
    */
    public class Agenda extends Activity {

    private final static String[] names = {“TODOS”,”VISITA”,”LLAMADA”,”CORREO”};

    private ArrayList actividades;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.agenda);

    try
    {
    ArrayAdapter adapter = new ArrayAdapter(this,
    android.R.layout.simple_spinner_dropdown_item,names);

    Spinner spActividades = (Spinner)findViewById(R.id.spActividades);

    spActividades.setAdapter(adapter);
    }
    catch (Exception e)
    {
    Log.e(“OnCreateSpiner”,e.getMessage());
    }

    try
    {
    fillActividades();

    ActividadesAdapter actividadesAdp;
    actividadesAdp = new ActividadesAdapter(Agenda.this,this.actividades);

    ListView lvActividades = (ListView)findViewById(R.id.lvActividades);

    lvActividades.setAdapter(actividadesAdp);

    }
    catch (Exception e)
    {
    Log.e(“OnCreateListView”,e.getMessage()+ ” -Causa: ” + e.getCause() + ” -“);

    }

    }

    public void onBackPressed()
    {
    finish();
    }

    private void fillActividades()
    {
    try {
    actividades.add(new Actividad(“VISITA”,”CONTACTO 1″,”1:00″,1));
    actividades.add(new Actividad(“CORREO”,”CONTACTO 2″,”2:00″,2));
    actividades.add(new Actividad(“LLAMADA”,”CONTACTO 3″,”3:00″,3));
    actividades.add(new Actividad(“VISITA”,”CONTACTO 4″,”4:00″,4));
    }
    catch (Exception e)
    {
    Log.e(“fillActividades”, e.getMessage());
    }

    }

    public class ActividadesAdapter extends ArrayAdapter
    {
    private Context context;
    private ArrayList datos;

    public ActividadesAdapter(Context context,ArrayList datos) {

    super(context,R.layout.item_agenda,datos);

    this.context = context;
    this.datos = datos;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent){

    LayoutInflater inflater = LayoutInflater.from(context);

    View item = inflater.inflate(R.layout.item_agenda,null);

    try
    {

    Actividad tmpActividad = (Actividad)this.datos.get(position);

    TextView _actividad = (TextView)item.findViewById(R.id.tvActividad);
    _actividad.setText(tmpActividad.getActividad());

    TextView _contacto = (TextView)item.findViewById(R.id.tvContacto);
    _contacto.setText(tmpActividad.getContacto());

    TextView _hora = (TextView)item.findViewById(R.id.tvHora);
    _hora.setText(tmpActividad.getHora());

    }
    catch (Exception e)
    {
    Log.e(“ActividadesAdapter”,e.getMessage());
    }

    return item;

    }
    }
    }

    1. Buenas Antonio.

      Sería interesante tener más información sobre el error, como línea en la que salta y qué tipo de error es, para poder ver el problema. A simple vista no he visto nada erróneo.

      Supongo que la aplicación no se te cerrará porque está todo entre try/catch, y que el error lo estarás viendo al saltar el bloque catch que sea. Para tener más información escribe “e.printStackTrace();” en los catch, de esta manera recibirás toda la información de la excepción que se produce y podrás depurar mejor tu código.

      Te hago un par de recomendaciones sobre tu código, porque tienes algunas prácticas que no son recomendables:
      – Tanto el ArrayList como el ArrayAdapter parametrízalos para indicar qué tipo de dato van a contener:
      ArrayList miColeccion = new ArrayList();
      public class ActividadesAdapter extends ArrayAdapter
      { … }
      Con esto te evitarás tener que usar algunos try/catch que tienes, que son innecesarios (ganarás eficiencia y limpieza en el código).
      – En el Adapter, reutiliza las vistas, comprueba si la vista es null y usa el inflater sólo en el caso de que sea null, si no lo es la podrás reutilizar, para eso tendrás que utilizar también el patrón holder. El no hacerlo con pocos elementos en las listas no da problema, pero cuanto más grande sea la colección de datos a mostrar en la lista más problemas de eficiencia al mostrarla. La forma de hacerlo está explicado justo en la siguiente entrada a esta: Programación Android, ListView & Adapter personalizado II

      Un saludo.

      1. Muchas gracias, hare caso a todas tus recomendaciones y te cuento que tal va, igual vere la siguiente entrada para optimizar el codigo y ver si omiti alguna configuracion

Deja un comentario