Crear un ListView personalizado en Android


Alguna vez han observado apps donde los elementos de una lista están conformados no solo de una sola línea de texto, sino que a veces podemos encontrarnos varios componentes como: un título, una pequeña descripción del tema, imágenes o botones, todos ellos en el mismo ítem como se muestra en la siguiente imagen.

Variada información por fila

Fig1: Variada información por fila

¿Cómo es posible aplicar este diseño a la lista?

Gracias a los ArrayAdapters, estos son la solución que le da a Android al trabajo con los ListView (Listas para visualizar contenido), es una especie de adaptador que nos permite mostrar los elementos de la lista junto con los datos de la forma que mejor deseemos.

Android contiene por defecto un ArrayAdapter para el trabajo con la lista, para utilizarlo solo tenemos que crearlo, insertarle el texto, el modelo y pasárselo a la lista. Quedaría el código algo como esto:

    /* Texto de ejemplo para mostrarlo en la lista */
    final String[] textos = new String[]{"Tex1","Tex2","Tex3","Tex4","Tex5"};

    ArrayAdapter adaptador = new ArrayAdapter(this, android.R.layout.simple_list_item_1, textos);

    ListView MiLista = (ListView) findViewById(R.id.milista);
    MiLista.setAdapter(adaptador);

En el snippet anterior creamos el ArrayAdapter que trae por defecto Android y le pasamos en sus parámetros el contexto, además de un layout (XML) genérico para los controles de tipo ListView (android.R.layout.simple_list_item_1), formado únicamente por un TextView con dimensiones predeterminadas que será el modelo que tendrá cada ítem de la lista y por último le pasamos el texto que queremos mostrar.

Por debajo el adaptador a cada ítem de la lista le inflara el XML simple_list_item_1 y asignara cada posición del arreglo textos a cada TextView de dicho modelo. Después capturamos la lista desde el visual y le asignamos el adaptador creado por nosotros.

Nota: No olvidemos insertar en el visual un componente de tipo ListView (Se puede hacer arrastrando la misma desde la paleta de componentes o insertar el siguiente código directamente en el XML).

    <ListView
        android:id="@+id/milista"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

La lista creada quedaría como se muestra en la siguiente imagen:

Lista de items

Fig2: Lista de items

¿Qué hacer si quisiéramos que cada elemento de la lista esté formado a su vez por varios elementos (imágenes, botones, varios textos, etc.)?

Como vemos la información que muestra la lista anterior es muy pobre, las Apps para móviles necesitan aprovechar el máximo de espacio por lo que recurrimos a esta técnica de mostrar en un mismo elemento varias informaciones.

A continuación les mostraré un ejemplo donde cada elemento de la lista estará compuesto por una imagen y dos textos organizados de tal manera como se muestra en la primera imagen de este artículo. Para este caso necesitamos crear nuestro propio ArrayAdapter y pasárselo a la lista.

Primeramente es necesario crear en un XML auxiliar que contendrá el aspecto general de cada uno de los ítem de la lista, para esto ir a la carpeta layout y presionar clic derecho encima de ella, paso seguido ir a New/Android XML File. Ingresamos el nombre del XML, en mi caso se llama lista_tema.

Una vez en el XML insertamos un ImageView y dos TextView. Luego lo organizaremos para que se muestre como en la imagen mostrada al principio. Aquí les dejo un ejemplo de cómo podría quedar:

// lista_tema.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >
  <ImageView
        android:id="@+id/ImagenArea1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:src="@android:drawable/star_big_on"/>
  

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/titulo"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:textColor="@android:color/black"
            android:textSize="30px"
            android:textStyle="bold" 
            android:width="210sp"
            android:maxWidth="210sp"/>

        <TextView
            android:id="@+id/descripcion"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:textColor="@android:color/black"
            android:textSize="12px"
            android:textStyle="normal" />
    </LinearLayout>   

</LinearLayout>

Nota: El componente ImageView como su nombre indica nos posibilita cargar imágenes. Estas pueden ser importadas desde el exterior hacia la carpeta drawable o cargadas desde @android:drawable que no es más que una dirección donde se encuentran varias imágenes que tiene por defecto el sistema Android. En este caso cargamos una imagen desde la SDK, utilizando la propiedad src

(android:src="@android:drawable/star_big_on")

Dentro del activity_main.xml solo hay que adicionar el ListView:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/fondo"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <ListView
        android:id="@+id/milista"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />  

</LinearLayout>

Como les comentaba anteriormente, el trabajo con lista implica la utilización de los ArrayAdapter, por lo que a continuación deberemos crear una nueva clase que haga función del mismo:

// AdaptadorLista.java:
public class AdaptadorLista extends ArrayAdapter{
// Hacemos que nuestra clase herede las características de un ArrayAdapter 
    
    Activity context;
    NameValuePair[] datos;
    /* Creamos las variables necesarias para capturar el contexto 
    *  y los datos que se publicarán en la lista
    */

    public AdaptadorLista(Activity context,NameValuePair[] datos) {
        super(context,R.layout.lista_tema,datos);
        this.context=context;
        this.datos=datos;
        // TODO Auto-generated constructor stub
    }

    /* Constructor de la clase, donde pasamos por parámetro los datos 
     * a mostrar en la lista y el contexto
    */
    public View getView(int position, View convertView, ViewGroup parent)
    {
        LayoutInflater inflater=context.getLayoutInflater();
        View item=inflater.inflate(R.layout.lista_tema, null);

        TextView title=(TextView) item.findViewById(R.id.titulo);
        title.setText(datos[position].getName());
        
        TextView descrip=(TextView) item.findViewById(R.id.descripcion);
        descrip.setText(datos[position].getValue().toString());
        
        return item;
    }   
}

El método getView es el más importante ya que es donde inflamos el fichero lista_tema.xml en cada ítem de la lista y después a ambos TextView dentro del fichero le insertamos los datos que queremos que muestre, este proceso se repite por cada ítem

Finalmente solo nos resta desde la clase principal hacer que el ListView utilice nuestro adaptador para visualizar los elementos:

// MainActivity.java:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        NameValuePair[] datosLista=new NameValuePair[4];
        datosLista[0]=new BasicNameValuePair("Java", "Soporta la POO.");
        datosLista[1]=new BasicNameValuePair("C++", "Soporta la POO.");
        datosLista[2]=new BasicNameValuePair("C","Soporta la POO.");
        datosLista[3]=new BasicNameValuePair("PHP","Soporta la POO.");

    /* Creamos los datos a presentar en la lista, elegimos un arreglo de pares. 
    * Este tipo de datos (NameValuePair) permite almacenar dos valores por lo que 
    * combinándolo con el arreglo podemos en cada posición del mismo almacenar dos 
    * cadenas de texto.
    */
                
        AdaptadorLista miAdaptador=new AdaptadorLista(this, datosLista);

    /* Creamos un objeto de el adaptador creado por nosotros anteriormente
    *  pasándole el contexto y los datos a mostrar
    */

        ListView lstOpciones = (ListView) findViewById(R.id.milista);
        lstOpciones.setAdapter(miAdaptador);

        /* Capturamos el ListView y le agregamos el adaptador */

    }
}

Si desean también ejecutar alguna acción al presionar sobre un ítem de la lista, solo tienen que crear el Listener relacionado con esta acción y el código a ejecutar:

lstOpciones.setOnItemClickListener(new OnItemClickListener() {
  @Override
  public void onItemClick(AdapterView<?> a, View view, int pos,long id) { 
    /* Código a ejecutar */         
}});

No olviden utilizar el parámetro pos para saber el ítem que presionó el usuario. Cualquier duda o sugerencia no dudes en dejar tu comentario. Como siempre puedes descargar el demo desde el siguiente enlace.

Demo List View