Tutorial De Listas Y Adaptadores En Android

Las listas en Android son contenedores supremamente útiles para organizar información en forma vertical y con la capacidad de usar scrolling (desplazamiento) para simplificar su visualización.

Esta técnica es muy popular en muchas aplicaciones, ya que permite mostrarle al usuario un conjunto de datos de forma práctica y accesible.

Google liberó un nuevo componente alternativo llamado RecyclerView para crear listas y grillas. Si deseas ver cómo usarlo ve al siguiente artículo: Crear una lista con RecyclerView

La Clase ListView En Android

La clase que representa una lista vertical en el API de Android se llama ListView. Esta clase viene preparada para recibir los ítems que desplegará en la interfaz, facilitando al programador la implementación de sus características y comportamientos.

Listas con la clase ListView en Android

Si en algún momento los ítems que contiene dificultan la visualización total en la actividad de la aplicación, automáticamente implementará scrolling para que el usuario pueda desplegar los elementos ocultos.

Estructuralmente un ListView contiene un View específico por cada fila. También se compone de un ScrollView, el cual permite generar el desplazamiento vertical por si se agota la pantalla para nuestros elementos.

CRM Leads App

Para darle ruta a los conocimientos que veremos a lo largo de este artículo, vamos a planificar una miniaplicación que resuelva alguna necesidad en especial, donde necesitemos crear una lista.

Se me ha ocurrido crear una app que muestre los Leads de un sistema online CRM (Custommer Relationship Management).

Esto suponiendo que los usuarios área de Marketing de alguna empresa hipotética, necesitan manejar la generación de leads para optimizar el flujo de ventas.

Wireframing de la característica

La idea es implementar una lista que muestre los siguientes datos de un lead:

  • Nombre
  • Cargo
  • Compañía a la que pertenece
  • Foto

Con ello en mente el siguiente es un bosquejo rápido con la ubicación de cada atributo:

Wireframing de app CRM Leads Android

Como ves, tendremos la foto (avatar) del lead en la parte izquierda centrada en el ítem. En la parte superior tendremos el nombre como texto primario. Y por debajo el cargo y la compañía como textos secundarios.

Descargar Proyecto Completo En Android Studio

Puedes descargar el proyecto final de CMR Leads en la siguiente caja de descargas:

Retomando, ahora veamos la programación…

Implementar Lista De Leads

Antes de iniciar, crea un nuevo proyecto en Android Studio llamado “CRM Leads”.

Crear actividad de leads

Añade una actividad del tipo Empty Activity y nombrala LeadsActivity.

El propósito de esta es actuar como el punto de entrada de la característica que vamos a crear.

Sobre ella añadiremos un fragmento, el cuál actuará como la implementación de la vista.

LeadsActivity.java

/**
 * Screen para el manejo de los leads en la app CRM
 */
public class LeadsActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leads);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        // Agregar fragmento

    }


}

Crear fragmento para mostrar lista de leads

Haz click derecho en el paquete java de tu proyecto, selecciona New > Fragment > Fragment (Blank) y confirma la creación de un fragmento con el nombre LeadsFragment.

LeadsFragment.java

/**
 * Vista para los leads del CRM
 */
public class LeadsFragment extends Fragment {
    
    public LeadsFragment() {
        // Required empty public constructor
    }

    public static LeadsFragment newInstance(/*parámetros*/) {
        LeadsFragment fragment = new LeadsFragment();
        // Setup parámetros
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            // Gets parámetros
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_leads, container, false);
       
        return root;
    }
}

Añadir ListView al layout

En la definición XML las listas se agregan con el elemento <ListView>. Debido a que esta clase extiende de ViewGroup, es posible que lo uses como nodo raíz de un layout.

Su definición se vería similar a esta:

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

 

Si quieres cambiar aspectos en su contenido o comportamiento puedes usar atributos XML como los siguientes:

Atributo Función
android:divider Drawable o color para representar el divisor entre ítems. Por defecto verás una línea horizontal de color gris claro. Usa el valor @null si no quieres que aparezca.
android:dividerHeight Altura del divisor
android:entries Aquí puedes poner la referencia de un array de strings de tus recursos para poblar automáticamente la lista sin usar adaptadores.
android:footerDividersEnabled Habilita o deshabilita el divisor que va antes de un elemento especial llamado “footer”, el cuál va al final de la lista.
android:headerDividersEnabled Similar a android:footerDividersEnabled, solo que esta vez se refiere al divisor de un elemento especial llamado “header” que va al inicio de la lista.

Teniendo esto en cuenta, abre el layout del fragmento fragment_leads.xml y situa un ListView dentro al interior de un FrameLayout.

fragment_leads.xml

<FrameLayout 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:paddingTop="8dp"
    tools:context="com.herprogramacion.crmleads.LeadsFragment">

    <ListView
        android:id="@+id/leads_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="@null" />

</FrameLayout>

Si abres la ventana Preview en la tab Text del editor de layouts verás lo siguiente:

Preview de lista de Leads en Android Studio

Agregar fragmento a la actividad

Abre LeadsActivity y sitúate en onCreate(). Llama al administrador de fragmentos y realiza una transacción add() con una instancia de LeadsFragment. Recuerda asignar un id al elemento XML de la actividad que actuará como container.

// ...
LeadsFragment leadsFragment = (LeadsFragment)
        getSupportFragmentManager().findFragmentById(R.id.leads_container);

if (leadsFragment == null) {
    leadsFragment = LeadsFragment.newInstance();
    getSupportFragmentManager().beginTransaction()
            .add(R.id.leads_container, leadsFragment)
            .commit();
}

Si ejecutas la aplicación en este momento tendrías una pantalla en blanco.

¿Por qué?

La respuesta a esa inquietud está en la siguiente sección…

Poblar Lista Con Un Adaptador

Un adaptador es un objeto que comunica a un ListView los datos necesarios para crear las filas de la lista. Es decir, conecta la lista con una fuente de información como si se tratase de un adaptador de corriente que alimenta a un televisor.

Además de proveer la información, también genera los Views para cada elemento de la lista.

Los adaptadores se representan programáticamente por la clase BaseAdapter. Dependiendo de la naturaleza de la lista se elegirá un adaptador prefabricado en el SDK de Android o extenderlos para satisfacer tus necesidades.

Interacción ListView-Adapter

Cuando relacionas un adaptador a una lista, inmediatamente comienza un proceso de comunicación interno para poblarla con una fuente de datos.

Dicha comunicación se basa principalmente en los siguientes métodos del adaptador:

  • getCount(): Retorna en la cantidad de elementos de la fuente de datos. Con este valor la lista ya puede establecer un límite para añadir items.
  • getItem(): Obtiene un elemento de la fuente de datos asignada al adaptador en una posición establecida. Normalmente la fuente de datos es una lista de objetos o un Cursor.
  • getView(): Retorna en el View inflado y ligado a los datos según su posición.

Aunque estos tres métodos no son los únicos que existen para establecer la relación, a mi parecer son los más significativos para entender el concepto de un adaptador.

El flujo de interacciones sería el siguiente:

L: — ¿Cuantos elementos hay para hoy?
A: — Existen “getCount()” elementos en la fuente
L: — ¡Comprendo!… muestrame el elemento 3
A: — Si claro, el contenido es “getItem(2)”
L: — Mmm…ya veo… ¿Y cuál sería su View?
A: — Al elemento 3 le corresponde “getView(2)”
L: — Ok, lo mostraré al usuario

Si aún no te ha quedado claro, entonces puedes ver esta ilustración sobre cómo sería esta relación:
Comunicación entre una lista y un adaptador

La clase ArrayAdapter

Como habíamos dicho, Android nos provee subclases de adaptadores que nos facilitan la implementación. ArrayAdapter es uno de los tipos de adaptadores más sencillos y populares.

Debido a que es una clase genérica, puedes usar cualquier objeto para poblar la lista.

Este adaptador infla los ítems con un layout preestablecido de Android, invoca el método toString() de cada elemento y lo setea automáticamente en uno o dos TextViews según el diseño.

ListView con elementos simples

Implementar ArrayAdapter de leads

Para implementar un ArrayAdapter en una lista simplemente seguimos los siguientes pasos:

1. Abre LeadsFragment y declara un atributo para la lista y otro para un ArrayAdapter con tipo String:

ListView mLeadsList;
ArrayAdapter<String> mLeadsAdapter;
//...

2. Obtén programáticamente una referencia de la lista en onCreateView():

// Instancia del ListView.
mLeadsList = (ListView) root.findViewById(R.id.leads_list);

3. Allí mismo crea un array de Strings con nombres de ejemplo e inicializa el adaptador con el constructor ArrayAdapter<T>(Context, int, T[]):

// ...
String[] leadsNames = {
        "Alexander Pierrot",
        "Carlos Lopez",
        "Sara Bonz",
        "Liliana Clarence",
        "Benito Peralta",
        "Juan Jaramillo",
        "Christian Steps",
        "Alexa Giraldo",
        "Linda Murillo",
        "Lizeth Astrada"
};

mLeadsAdapter = new ArrayAdapter<>(
        getActivity(),
        android.R.layout.simple_list_item_1,
        leadsNames);

Donde los parámetros del constructor tienen el siguiente propósito:

  • Context context: Representa el contexto de la aplicación. Usamos getActivity() para indicar que será la actividad.
  • int resource: Es el recurso de diseño o layout que representará cada fila de la lista. En este caso usamos un recurso del sistema llamado simple_list_item_1.xml. Este layout contiene un solo TextView que contendrá el texto de cada fila. Si deseas dos líneas de texto, usa simple_list_item_2.xml.
  • T[] objects: Array genérico con los datos a inflar en la lista. También puedes usar una lista List<T>.

4. Vincula la lista con el adaptador a través del método ListView.setAdapter():

mLeadsList.setAdapter(mLeadsAdapter);

Esta referencia inicia el proceso de llenado de la lista.

Por lo que al ejecutar la app CRM en este punto tendrás:

CRM Leads

Este no es el diseño propuesto al inicio, sin embargo es un buen ejemplo de ArrayAdapter con un diseño prefabricado.

Crear tu propia fuente de datos

Si la información que vas a usar en los elementos de la lista tiene formatos especiales o un manejo complejo, es mejor crear una nueva clase que represente a cada ítem.

Esto es sencillo, solo crea una nueva clase llamada Lead y agrega los atributos que vimos al inicio del problema junto a un ID.

Luego autogenera el constructor, gets, sets y el método toString():

Lead.java

/**
 * Entidad Lead
 */
public class Lead {
    private String mId;
    private String mName;
    private String mTitle;
    private String mCompany;
    private int mImage;

    public Lead(String name, String title, String company, int image) {
        mId = UUID.randomUUID().toString();
        mName = name;
        mTitle = title;
        mCompany = company;
        mImage = image;
    }

    public String getId() {
        return mId;
    }

    public void setId(String mId) {
        this.mId = mId;
    }

    public String getName() {
        return mName;
    }

    public void setName(String mName) {
        this.mName = mName;
    }

    public String getTitle() {
        return mTitle;
    }

    public void setTitle(String title) {
        this.mTitle = title;
    }

    public String getCompany() {
        return mCompany;
    }

    public void setCompany(String mCompany) {
        this.mCompany = mCompany;
    }

    public int getImage() {
        return mImage;
    }

    public void setImage(int mImage) {
        this.mImage = mImage;
    }

    @Override
    public String toString() {
        return "Lead{" +
                "ID='" + mId + '\'' +
                ", Compañía='" + mCompany + '\'' +
                ", Nombre='" + mName + '\'' +
                ", Cargo='" + mTitle + '\'' +
                '}';
    }
}

Crear layout personalizado para ítem de la lista

Crea un nuevo layout llamado list_item_lead.xml.

Establece un RelativeLayout como nodo e intenta generar las ubicaciones de cada dato del lead como se muestra en el siguiente mock de alto nivel:

Ítem de lista personalizado con imagen y texto

El siguiente sería el resultado final:

list_item_lead.xml

<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
    android:minHeight="?attr/listPreferredItemHeight"
    android:orientation="vertical"
    android:paddingBottom="8dp"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="8dp">

    <ImageView
        android:id="@+id/iv_avatar"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_centerVertical="true" />

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="56dp"
        android:text="Nombre"
        android:textAppearance="?attr/textAppearanceListItem"
        tools:text="Alexander Pierrot" />

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/tv_name"
        android:layout_below="@+id/tv_name"
        android:text="Cargo"
        tools:text="CEO" />

    <TextView
        android:id="@+id/tv_company"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/tv_name"
        android:layout_below="@+id/tv_title"
        android:text="Compañía"
        tools:text="Blue Insurances Ltd" />
</RelativeLayout>

Donde los siguientes son aspectos a resaltar:

  • Usa el atributo prestablecido ?attr/listPreferredItemHeight para evitar que la altura mínima del ítem sea menor a las normas de diseño en Android.
  • El nombre del lead es el texto primario del ítem. Evita acomodar manualmente su estilo con la apariencia predefinida ?attr/textAppearanceListItem.
  • El atributo tools:text permite renderizar texto experimental en la ventana Preview, sin tener que ejecutar la app para ver el resultado.

Crear adaptador personalizado

1. El adaptador que tienes hasta ahora no funciona para cambiar el contenido de la imagen y los tres textos. Esto te obliga a sobrescribir ArrayAdapter.

Para ello crea una nueva clase Java llamada LeadsAdapter y extiéndela del tipo ArrayAdapter<Lead>.

public class LeadsAdapter extends ArrayAdapter<Lead> {

2. Luego crea un constructor que reciba el contexto y la lista leads. Llama al constructor padre que usamos anteriormente con super() y agrega los dos parámetros entrantes. Debido a que el segundo parámetro es el layout pasa 0, ya que usaremos un propio.

3. El método getView() es el que infla los ítems y liga los datos de los objetos Lead, así que el objetivo es sobrescribir este método.

Los parámetros que recibe son:

  • int position: La posición actual del elemento a procesar
  • View convertView: El view actual a procesar. Puede ser null si no se ha inflado aún.
  • ViewGroup parent: El padre al cual el ítem será insertado, en este caso será el ListView.

Con esto en mente, la receta a seguir sería:

  1. Obtener referencia del componente LayoutInflater.
  2. Inflar el layout del ítem si el view no existe
  3. Obtén las instancias de los views a modificar
  4. Obtén la instancia actual de la fuente de datos, extrae sus valores y haz el setup en los views.

Veamos:

/**
 * Adaptador de leads
 */
public class LeadsAdapter extends ArrayAdapter<Lead> {
    public LeadsAdapter(Context context, List<Lead> objects) {
        super(context, 0, objects);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // Obtener inflater.
        LayoutInflater inflater = (LayoutInflater) getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        // ¿Existe el view actual?
        if (null == convertView) {
            convertView = inflater.inflate(
                    R.layout.list_item_lead,
                    parent,
                    false);
        }

        // Referencias UI.
        ImageView avatar = (ImageView) convertView.findViewById(R.id.iv_avatar);
        TextView name = (TextView) convertView.findViewById(R.id.tv_name);
        TextView title = (TextView) convertView.findViewById(R.id.tv_title);
        TextView company = (TextView) convertView.findViewById(R.id.tv_company);

        // Lead actual.
        Lead lead = getItem(position);

        // Setup.
        Glide.with(getContext()).load(lead.getImage()).into(avatar);
        name.setText(lead.getName());
        title.setText(lead.getTitle());
        company.setText(lead.getCompany());
       
        return convertView;
    }
}

La librería Glide permite cargar imágenes de forma asíncrona para no entorpecer el hilo principal de la UI. Para usarla agrega la siguiente dependencia a build.gradle a nivel de módulo:

compile 'com.github.bumptech.glide:glide:3.7.0'

 

Crear repositorio de datos

Lo último que falta es generar los leads que añadiremos a nuestro a adaptador.

Crea una nueva clase Java con patrón Singleton que represente un origen de datos ficticio del cual se alimentará el adaptador. Usa el nombre LeadsRepository.

LeadsRepository.java

/**
 * Repositorio ficticio de leads
 */
public class LeadsRepository {
    private static LeadsRepository repository = new LeadsRepository();
    private HashMap<String, Lead> leads = new HashMap<>();

    public static LeadsRepository getInstance() {
        return repository;
    }

    private LeadsRepository() {
        saveLead(new Lead("Alexander Pierrot", "CEO", "Insures S.O.", R.drawable.lead_photo_1));
        saveLead(new Lead("Carlos Lopez", "Asistente", "Hospital Blue", R.drawable.lead_photo_2));
        saveLead(new Lead("Sara Bonz", "Directora de Marketing", "Electrical Parts ltd", R.drawable.lead_photo_3));
        saveLead(new Lead("Liliana Clarence", "Diseñadora de Producto", "Creativa App", R.drawable.lead_photo_4));
        saveLead(new Lead("Benito Peralta", "Supervisor de Ventas", "Neumáticos Press", R.drawable.lead_photo_5));
        saveLead(new Lead("Juan Jaramillo", "CEO", "Banco Nacional", R.drawable.lead_photo_6));
        saveLead(new Lead("Christian Steps", "CTO", "Cooperativa Verde", R.drawable.lead_photo_7));
        saveLead(new Lead("Alexa Giraldo", "Lead Programmer", "Frutisofy", R.drawable.lead_photo_8));
        saveLead(new Lead("Linda Murillo", "Directora de Marketing", "Seguros Boliver", R.drawable.lead_photo_9));
        saveLead(new Lead("Lizeth Astrada", "CEO", "Concesionario Motolox", R.drawable.lead_photo_10));
    }

    private void saveLead(Lead lead) {
        leads.put(lead.getId(), lead);
    }

    public List<Lead> getLeads() {
        return new ArrayList<>(leads.values());
    }
}

Tendremos 10 ejemplos para probar la lista. También habrá un método para guardar nuevas instancias saveLead() y otro para retornar en todos los objetos del repositorio getLeads().

Cabe aclarar que los avatares estarán en los recursos drawables, por lo que guardamos su identificador entero como referencia.

Integrar todo en la aplicación

Ahora solo queda volver a LeadsFragment, crear una instancia del nuevo adaptador y añadir los datos del repositorio.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.fragment_leads, container, false);

    // Instancia del ListView.
    mLeadsList = (ListView) root.findViewById(R.id.leads_list);

    // Inicializar el adaptador con la fuente de datos.
    mLeadsAdapter = new LeadsAdapter(getActivity(),
            LeadsRepository.getInstance().getLeads());

    //Relacionando la lista con el adaptador
    mLeadsList.setAdapter(mLeadsAdapter);

    return root;
}

Ejecuta y verás el siguiente resultado:

Sistema CRM para manejo de Leads

 

Estamos probando un ejemplo estático. En casos reales es posible que los datos vengan de una base de datos SQLite, un Web Service u otra fuente de datos concurrente.

Actualizar un ListView

Si en algún momento los datos de la lista cambian, el encargado de actualizar los elementos será el adaptador. El método que informa dichas modificaciones es notifyDataSetChanged(). Cuando se llama, los views son refrescados y así el usuario percibe el cambio en tiempo real.

Este método es ejecutado automáticamente por el adaptador cuando se llaman métodos que modifiquen la fuente de datos.

Algunos son:

  • add(): Añade un nuevo elemento al final de la lista.
  • insert(): Añade un nuevo elemento en una posición especificada de la lista.
  • remove(): Elimina un elemento de la lista
  • clear(): Elimina todos los elementos de la lista.

Por ejemplo…

Podemos añadir un Action Button a la Action Bar llamado “ELIMINAR TODO”, el cual tendrá como fin limpiar todos los elementos de la lista a través del método clear() del adaptador.

Esto requiere que:

1. Habilites la contribución del fragmento a la action bar con el método setHasOptionsMenu en onCreateView():

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    // ...

    setHasOptionsMenu(true);
    return root;
}

2. Crees un nuevo recurso de menú llamado menu_leads_list.xml con el ítem de eliminación:

menu_leads_list.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_delete_all"
        android:title="@string/action_delete_all"
        app:showAsAction="ifRoom" />
</menu>

3. Infles el menú en el fragmento con onCreateOptionsMenu():

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);
    inflater.inflate(R.menu.menu_leads_list, menu);
}

4. Proceses el evento del click con onOptionsItemSelected():

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    if (id == R.id.action_delete_all) {
        // Eliminar todos los leads
        mLeadsAdapter.clear();
        return true;
    }
    return super.onOptionsItemSelected(item);
}

Al probar veremos que automáticamente los elementos de la lista han sido erradicados:

Clear ListView Android

Manejar Eventos De Click En Items

Si deseas ejecutar acciones cuando el usuario selecciona algún ítem de la lista, entonces debes setear una escucha del tipo OnItemClickListener.

Esta interfaz permite disparar el método onItemClick(), el cual nos permitirá ejecutar las acciones que deseamos automatizar.

Por ejemplo…

Abre LeadsFragment y dentro de onCreateView() setea una escucha anónima con setOnItemClickListener(). En el controlador despliega un Toast para mostrar el nombre del ítem clickeado.

// Eventos
mLeadsList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
        Lead currentLead = mLeadsAdapter.getItem(position);
        Toast.makeText(getActivity(),
                "Iniciar screen de detalle para: \n" + currentLead.getName(),
                Toast.LENGTH_SHORT).show();
    }
});

onItemClick() recibe tres parámetros. El primero es el View que usa al adaptador, en este caso la lista. El segundo es el View del ítem que ha sido presionado, el tercero hace referencia a la posición del ítem en la fuente de datos que maneja el adaptador y el cuarto es un identificador del elemento.

Simplemente obtuvimos el ítem Lead presionado en el adaptador con la posición de entrada. Luego de ello construimos una cadena que indique el nombre. Y finalmente desplegamos en pantalla un Toast con los datos:

Procesar eventos de click en lista

Un Toast es una pequeña ventana emergente con una duración determinada, el cual despliega un mensaje para notificar al usuario el cumplimiento de alguna acción. Su método makeText() genera una instancia prefabricada para simplificar la creación.

Personalizar el Selector de una Lista

¿Qué es un StateListDrawable?

Los fondos que se proyectan en un View al interactuar con la interfaz de usuario, son representados por un objeto drawable llamado StateListDrawable. Este está compuesto por una serie de drawables relacionados a un estado de interacción del componente.

¿Has visto que los ítems de un Listview toman un color azul al ser presionados?, cuando está iluminado se le llama estado de Presionado (pressed).

Ese fondo que toma momentáneamente el ítem es generado a través de un StateListDrawable predeterminado por los temas y estilos que estemos utilizando. Existen otros estados como “Presionado sostenidamente“(long pressed), Enfocado (focusabled), Seleccionado (selected), etc.

Crear un Selector para un ListView

Para declarar previamente el StateListDrawable de un view antes de inflarlo, podemos usar un archivo de recursos de tipo drawable. Donde el nodo que relacionará las imágenes con la lista de estados es representado por el elemento <selector>. Dentro de él declararemos elementos <item> que contendrán la información sobre cada drawable.

Veamos un ejemplo:

item_list_indicator.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_pressed="true"
        android:drawable="@drawable/list_pressed" /><!-- Pressed -->
</selector>

La anterior descripción asigna un drawable al estado stated_pressed (presionado).

Para habilitar la transición de fondo, el estado debe tener el valor de true.

En el atributo drawable puedes usar una referencia a tus recursos. La práctica común es crear NinePatches para la flexibilidad del fondo en cualquier tipo de dimensiones y densidades (si descargas el código verás los que se usaron).

Una vez creada tu lista de estados, pasamos a asignarla al ListView. Para ello usamos el atributo listSelector, el cual representa la lista de estados.

<ListView
    android:id="@+id/leads_list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:divider="@null"
    android:listSelector="@drawable/item_list_indicator"/>

Si ejecutas y presionas un ítem verás:

List selector personalizado en ListView

Ripple Effect En ListView

Por otro lado, puedes asignar un efecto ripple al presionar los elementos.

Ripple Effect en ListView Android

Recuerda que esta es una tendencia de Material Design para transmitir al usuario que los ítems actúan como superficies que reaccionan físicamente ante el contacto.

Para hacerlo puedes crear tu propio elemento ripple:

<code><span class="com"> <ripple android:color="?android:attr/colorControlHighlight">
   <item android:id="@android:id/mask"
         android:drawable="@android:color/white" />
 </ripple></span></code>

O usar en el background del nodo principal del ítem de lista el atributo ?attr/selectableItemBackground.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:background="?attr/selectableItemBackground"">

Patrón ViewHolder Para Optimizar La Lista

Un adaptador personalizado llama un gran cantidad de veces el método findViewById() para obtener las referencias de los views del ítem.

Para reducir este trabajo considerablemente, Google recomienda usar el patrón ViewHolder. Una clase que almacena las referencias de los views hijos del ítem, con el objetivo de acceder a ellas en el futuro.

Este consiste en:

  • Crear una clase anidada dentro del adapter con referencias a los views
  • Comprobar en getView() si las referencias ya fueron guardadas
    • Si no es así, entonces se crea un nuevo ViewHolder para salvarlas
    • De lo contrario, se usan las referencias existentes

Con este flujo, LeadsAdapter quedaría así:

LeadsAdapter.java

/**
 * Adaptador de leads
 */
public class LeadsAdapter extends ArrayAdapter<Lead> {
    public LeadsAdapter(Context context, List<Lead> objects) {
        super(context, 0, objects);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // Obtener inflater.
        LayoutInflater inflater = (LayoutInflater) getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        ViewHolder holder;

        // ¿Ya se infló este view?
        if (null == convertView) {
            //Si no existe, entonces inflarlo con image_list_view.xml
            convertView = inflater.inflate(
                    R.layout.list_item_lead,
                    parent,
                    false);

            holder = new ViewHolder();
            holder.avatar = (ImageView) convertView.findViewById(R.id.iv_avatar);
            holder.name = (TextView) convertView.findViewById(R.id.tv_name);
            holder.title = (TextView) convertView.findViewById(R.id.tv_title);
            holder.company = (TextView) convertView.findViewById(R.id.tv_company);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        // Lead actual.
        Lead lead = getItem(position);

        // Setup.
        holder.name.setText(lead.getName());
        holder.title.setText(lead.getTitle());
        holder.company.setText(lead.getCompany());
        Glide.with(getContext()).load(lead.getImage()).into(holder.avatar);

        return convertView;
    }

    static class ViewHolder {
        ImageView avatar;
        TextView name;
        TextView title;
        TextView company;
    }
}
  • Justin Vera

    Hola James que tal… muy buen blog =D… tengo un problema al ejecutar el proyecto me sale el siguiente error =(… porfa espero contar con su ayuda. Gracias https://uploads.disquscdn.com/images/bd61edfe029bb444972985551dc3c88756990408219572b1a20db31e40f9ebc1.jpg

    • Hola Justin, parece que hay un id de algun elemento que no coincide con su llamada. Si modificaste algo fijate que cuando llames dicho identificador el valor sea el correcto.

      • Justin Vera

        Ya vi mi error, era porque no cree el fragmento en el activity_leads.xml por eso no encontraba el id.
        Muchas gracias por responder.

        Pd: Le sugiero que ponga esa parte, pero de ahi todo bien con sus pasos me sirvio muchisimo gracias nuevamente =D

  • Marco Antonio Pérez Hernández

    hola ya no esta el linl para descargar?? buenos tutoriales

  • Pingback: Tutorial De Listas Y Adaptadores En Android | H...()

  • Eduardo Heredia

    Felicidades por tu sitio. Los contenidos son claros, concisos, bien explicados.

    Saludos !

  • angela

    hola Amigo, primero felicitarte por la colaboración que nos brindas al hacer este excelente aporte, bueno soy principiante en esto es por ello que estoy haciendo pruebas con tu código. descargue el código y lo estoy haciendo correr en eclipse y me da error en lista.setOnItemClickListener(this); para capturar un Item, y me sale el error de the method setOnItemclicklistener in the type AdapterView is not applicable, por favor tal ves podrías indicarme el por que?

    • Hola Angela, fijate en el import de la clase en la parte superior del archivo es AdapterView.OnClickListener?

  • Miguel Angel Bolaños

    tengo un scrollview con n botones , cada botón tiene asignado un listview, al dar click en el boton(i) , debe apagar todos los listview y solo dejar “vivo”el listview(i), mi problema es que solo aparece el listview(1) y al dar click en el botón(J), desaprece el listview(1) , pero no presenta el listview(j)

    public void boton_filtro_click(View btn){

    Button b1 = (Button)btn;

    int nPos = btn.getId();

    for ( int i=0 ; i < _lv_filtro.length ; i++){

    _lv_filtro[nPos]._lv.setVisibility(btn.GONE);

    if ( nPos== i ){

    Toast.makeText(getApplicationContext(),"Boton ["+nPos + "]["+_lv_filtro.length+"]["+_lv_filtro[nPos]._lv.getAdapter().getItem(1).toString()+"]["+((Button) btn).getText().toString(),Toast.LENGTH_SHORT).show();

    _lv_filtro[nPos]._lv.setVisibility(btn.VISIBLE);

    // _lv_filtro[nPos]._lv.findViewById(nPos).setVisibility(View.VISIBLE);

    }

    }

    // _lv_filtro[5]._lv.setVisibility(View.VISIBLE);

    }

  • Alvaro Valenzuela

    Hola, muchas gracias por tu aporte… Tengo una duda, necesito crear un listview que parsee un JSON con diferentes objetos y que muestre algo como la imagen adjunta… alguna sugerencia, ayuda o referencia?

    De antemano muchas gracias

  • Alvaro Valenzuela

    Hola, muchas gracias por tu aporte… Tengo una duda, necesito crear un listview que parsee un JSON con diferentes objetos y que muestre algo como la imagen adjunta… alguna sugerencia o referencia?
    De antemano muchas gracias

  • Mario German Agudelo

    Hola, gracias por compartir tu conocimiento: la idea es que en la actividad principal existan dos botones los cuales al darle click ellos me envíen a la actividad del recyclerview el cual sería el mismo para los dos botones, lo unico es que al darle click en el botón 1 muestre la información solo de ese botón y en el botón 2 al darle click se muestre solo la información de ese botón, agradezco toda la información que me puedas dar, gracias.

  • Jonny

    Hola, ¿quisiera saber cuándo es recomendable usar ListView vs RecyclerView para hacer listas?

    Gracias por la atención.

  • W Fabiian Rojas

    Como Hago Para Llenar DataSource Pidiéndole La Información Al Usuario

  • Daniel Irias

    Cómo podría reemplazar el Static del DataSource para llenarlo con Volley??

    static{

    TAREAS.add(new Tarea(“Trotar 30 minutos”,”08:00″));
    TAREAS.add(new Tarea(“Estudiar análisis técnico”,”10:00″));
    TAREAS.add(new Tarea(“Comer 4 rebanadas de manzana”,”10:30″));
    TAREAS.add(new Tarea(“Asistir al taller de programación gráfica”,”15:45″));
    TAREAS.add(new Tarea(“Consignarle a Marta”,”18:00″));

    }
    No tengo Problemas con la conexión usando Volley. Donde tengo problema es al intentar usarla dentro de este adaptador.
    private RequestQueue requestQueue;

    //Instancia única de la cola de peticiones
    requestQueue = RequestQueueSingleton.getRequestQueue(this);

    requestQueue Me devuelve el siguiente Error: Non-static field ‘requestQueue’ cannot be referenced from a static context.

    this Me devuelve el siguiente Error: …bla bla..cannot be referenced from a static context.

    AYUDAAAA!! NECESITO LLENARLO CON UN JSONArray que traigo con Volley.

  • Julio Chavez Alvaro

    Hola interesante, tengo un problema, estoy creando mi proyecto con reproductor de musica, que recoge datos de json, cuando lo ejecuto aparecen el listview poblado de datos con el autor, portada, duracion y un boton de play para escuchar la musica, quisiera que me orientes como puedo hacer para que al momento de presionar play en cualquier elemento del listview y volver a presionar en otro el anterior este primero detenga el sonido ya que si se ejecutan 2 elementos con musica al mismo tiempo, hace se cierre mi aplicacion, muchas gracias

    • Arturo

      Puedes crear un método que se llame reproducirMusica que acepte como parámetros un View y obtener el id del elemento desde su Onclick en el xml,
      dentro de el creas un switch que se ejecute según el elemento clickeado.

      Ahora que si quieres que se ejecute uno después del otro puedes ocupar un hilo que si se esta ejecutando alguno lo pare e inicie el siguiente.

      ahora que silo que quieres es pararlo toma el esta en ejecución desde sumetodo Stop

  • Kais Harim del Pino

    esto es lo que me aparece

  • Kais Harim del Pino

    Veo que el problema es por la versión del android studio. En mi versión la carpeta drawable no es accesible, y por tanto he puesto las imagenes directamente en mipmap-mdpi. Por tanto ahora al poner android.R.[aquí me da la opción de poner mipmap].ic_health pero no detecta la imagen… ¿Alguna solución?

  • Kais Harim del Pino

    Hola! antes que nada, gracias por este magnífico tutorial.
    El problema que tengo, es que en el “DataSource” al añadir la imagen no la encuentra (yo las tengo en mipmap-mdpi)

  • Carolina Choque Coronel

    hola James este link tambien esta roto

  • Me da error tareaArrayAdapter is not a generic type

    • Porfin he podido arreglar el código par que funcione

      public class TareaArrayAdapter extends ArrayAdapter

  • Puedes subir el código fuente? no queda muy claro donde tienes que poner cada trozo de código o archivos etc…

  • Pedro A.

    Hola, antes de nada muchas gracias por tus tutoriales. En este artículo me he quedado atascado en la última parte. No consigo que los items de la lista cambien de color.

    list_pressed no está definido. Yo creo un archivo Resource XML y dentro defino un color para list_pressed. Cuando pulso sobre un ITEM de la Lista se abre el menu contextual pero el item no ha cambiado de color.

    #CA000000

    He descargado el código de ejemplo y no veo donde das valor a list_pressed.

    Por otro lado(y esto ya es tema aparte que tengo que estudiar mejor)por mas vueltas que le doy soy incapaz de importar el código en Android Studio o en Eclipse ni de tu página ni de casi ninguna.

  • Brandon Huerta

    Hola, esta mas que perfecto el post, muchisimas gracias, me encantaria saber si es posible hacer esto desde una base de datos de SQLite, y de igual forma, poder abrir una ventana al tocar un elemento de la lista que contenga informacion adicional (jalando la desde la misma base de datos).

  • Lorena S.

    Genial el tuto. Muy bien explicado todo.
    Te hago una consulta… Qué herramienta usas para hacer el boceto? (El de cada item de la lista por ejemplo).

    Gracias!! :D

    • Gracias por tu apreciación Lorena. Esas imágenes las hice a mano con https://pixlr.com/editor/. Aunque este post es antiguo y la verdad me quedaron regulares :v. Sin embargo para bocetos puedes usar ninjamock.com. Otra buena es pickmonkey.

  • Muchas gracias !!!1 , definitivamente hermosaprogramacion esta dándome mi carrera ajajjajaj .

    • jajajajaj no vaya pagando no más. jajaa mentiras, me alegra que te sirvan los tutoriales.

  • Esme Salas

    Hola que tal… ya resolví mi problema… Pero ahora tengo otra inquietud:
    Como harías para poner un ListView dentro de un Fragment? me podrías orientar un poco??
    Gracias

    • Hola Esme.

      Pondría en el layout del fragmento una etiqueta como nodo principal. Luego en el método onCreateView() obtendría la lista, algo como:

      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
      View view = inflater.inflate(R.layout.fragmento, container, false);

      lista = (ListView) view.findViewById(R.id.lista);

      //Obvio inicializas el adaptador antes
      lista.setAdapter(adaptador);

      return view;
      }

  • Nicole Madariaga

    Estaba tratando de mostrar en dos lineas extrayendo los datos de una base datos MySql y solo me resultaba mostrando una linea pero tu aporte me solucionó el problema, muchas gracias !!!

  • Leo HR

    Hola saludos,

    Estoy hasta antes del tema Actualizar un List View, ya tengo el código
    correcto, no marca ningun error, pero a la hora de correrlo, el
    prorama se detiene, no se lanza.

    Revise linea por linea y lo unico que tengo diferente pues es el nombre de
    las imagenes. La dimención de las imagenes es de 30×30

    Podria darme alguna sugerencia o aclaración del porqué la app no se lanza?

    Gracias

    • Leo HR

      Ya lo resolví, felicidades por el sitio, me esta aportando mucho. Saludos

      • Hola Leo, que pena la demora. Espero poder responder más rápido para la próxima. Saludos!

      • Heber Jimenez Mendez (naen)

        Hola Leo, estoy teniendo el mismo problema que tuviste, se detiene la aplicacion al tratar de cargarla… estoy tambien antes del tema de actualizar. Agradeceria mucha tu ayuda.

        • ¿Que error te lanza el logcat amigo?

          • Heber Jimenez (naen)

            Amigo James gracias por responder, todo el codigo esta bien. pero al momento de iniciar el emulador sale paqute has stopped unexpectedly. please try again. estoy que le doy vuelta a esto y no hallo solucion…

          • Puedes pegar el pantallazo?

          • Heber Jimenez (naen)

            Amigo James acá está la imagen…te comento que lo volví hacer de nuevo todo el código…y el mismo resultado tira

          • Me refiero a un pantallazo del logcat amigo :D

          • Heber Jimenez (naen)

            XD

          • Creo que estás intentando acceder a un elemento del adaptador con un indice superior al número de elementos que hay o un indice negativo (ej. -1). ¿Sabes donde pueda estar pasando?

        • Leo HR

          Hola Heber, una enorme enorme disculpa por no responderte, no me había dado cuenta.

          Pues mira el problema por el que no se lanzaba mi aplicación era que en mi archivo TareaArrayAdapter, a la hora de obtener las instancias de los textviews yo estaba haciendo referencia a los recursos propios de android, algo como:

          TextView titulo = (TextView)listItemView.findViewById(android.R.id.text1);
          ———-

          …………cuando debería ser así, sin el “android”

          TextView titulo = (TextView)listItemView.findViewById(R.id.text1);

          Ese fue mi caso amigo Heber. Si no me expliqué con las palabras correctas, se que nuestro buen amigo James pueda explicarte claramente lo que trate de decir jeje

          Saludos :)

          • Heber Jimenez (naen)

            Gracias Leo por tu respuesta, pero al parecer no es mi problema eso, en mi caso, todo el código está limpio, solo al correrlo se detiene y no carga la aplicación.

  • Esme Salas

    Hola buenas noches:

    Tengo un problema amigo, ya tengo todos los archivos como tu los presentas en el articulo,

    pero mi TareaArrayAdapter.java no recoce el archivo image_list_item me aparece en letras rojitas:

    listItemView = inflater.inflate(android.R.layout.image_list_item1, parent, false);

    ¿me podrías ayudar?

    Muchas Gracias =(

    • Hola Esme.

      ¿No será por que tienes ese uno al final de “image_list_item1“?

      • Esme Salas

        Hola:
        Perdón se me fue =) lo que pasa que modifique el nombre para ver si lo corregia pero tampoco lo reconoció. de igual manera lo dejé como tu lo muestras en tu articulo pero nada!! :(

        • Parece que cambiaste el nombre manualmente sin refactorizar. Al hacer eso el archivo R.java pierde las referencias de los identificadores.

          Prueba darle rebuild project o clean project.

  • Angel

    Muy buenas a tod@s.

    Articulo muy completo, pero me hubiera venido muy bien la actualización de la lista después de una modificación.
    En mi caso, he listado los datos que tengo en una base de datos sobre Pacientes.
    Al pulsar en cualquier item de la lista, este se despliega en un dialogo donde puedo borrar o modificar. En este ultimo caso, se lanza otra actividad donde se muestran los datos que tengo y que puedo modificar y guardar. Se guarda directamente en la base de datos con lo que, al volver a la actividad donde tengo la lista, esta no se actualiza y me sigue mostrando los datos sin modificar.
    Es cuando cierro dicha actividad y vuelvo a lanzarla se cargan los datos correctamente.
    Mi cuestión es como realizar esa actualización tras la modificación (pulso un boton de “Aceptar”, se guardan los datos en la base de datos y se cierra con “finish()” pasando a la actividad anterior que es la que esta con el ListView).
    Muchas gracias.

    • James Revelo

      Hola Angel.

      Una de las formas mas simples que conozco es iniciar la actividad con startActivityForResult() y luego recibir un dato booleano en onActivityResult(), que indique si es necesario recrear la actividad con la lista, lo que actualizaría los datos.

      Por otro lado, si pones el botón de guardar como Up Button, la actividad anterior se recreará automáticamente al presionarlo, por lo que los datos se actualizarán.

      También podrías encerrar tu base de datos con un Content Provider como fachada. Este elemento puede coordinarse con un Cursor Loader para cargar los datos en tu lista. Lo que revisa automáticamente si el curso de la consulta cambió y actualizar automáticamente los datos.
      Te dejo un tutorial de content providers:http://www.hermosaprogramacion.com/2015/06/tutorial-android-crear-un-content-provider-personalizado/

      Saludos amigo!

      • Angel

        Hola!!
        Muchas gracias por responder.
        Lo he solucionado tal como me has comentado, con el startActivityForResult().
        Después de dar tantas vueltas y buscar mucha información, la solución ha sido bien sencilla y con apenas código. Genial!
        Muchísimas gracias.
        Un saludo!!

        • James Revelo

          Que bueno que te haya servido, saludos!

          • Pedro Acosta

            James me podrías ayudar tambien jeje, mi problema es parecido, imagínate que tengo una lista en el adapter y cada item de la lista es un “check” cuando hace click yo modifico la lista (true/false), si el usuario quiere cancelar esos datos “se hicieron” cosa que no quiero cuando guardo si modifico base de datos y todo bien, mi problema es dejar todo como estaba. gracias. Me avisas si no me explique bien

          • jajaja no te entendí nada compañero :v

  • Jose Alberto

    Hola!!

    Muy bueno el artículo. Aquí está mi duda. Me gustaría rellenar el listview con un array definido en fichero strings.xml en lugar de con TAREAS.add(new Tarea(“Trotar 30 minutos”,”08:00″,R.drawable.ic_health)); . ¿Cómo tendría que modificar el adaptador? Muchas gracias por adelantado!!

    • James Revelo

      Hola Jose.

      Usa el método estático adaptador = ArrayAdapter.createFromResource(this, R.array.TuArray, LayoutItemDeAndroid);

    • Jose Alberto

      Muchas gracias por tu respuesta, voy a probar!!

      • Jose Alberto

        Hola de nuevo James!

        Tengo otra duda con esto de los listview. He hecho un listview cuyo layout es uno de los que te da android: simple_list_item_1 . No he tendio problema a la hora de crear el adapter y todo lo demás, pero me gustaría saber si se puede cambiar el ancho de las filas, el tipo de letra, el color etc, o ya tendría que crear uno customizado.

        Muchas gracias de nuevo!!

  • Jose A.

    Muy buena la pagina. Necesitaria me echaras una mano con un problema que tengo con un listivew. Este, solo tiene una linea que obtengo de un campo de una sqlite, lo saco con un SimpleCursorAdapter y le he puesto simple_list_item_1, lo que pretendo es que al pulsar un boton me pase la información de ese campo a otra pantalla, que eso sera otro problema para mi, pero que ahora, no me preocupa. Cuando getItemAtPosition(position) me sale otra informacion que no es la que necesito como indicas en “Tarea@123422″ pero mas largo y con el nombre de …sqlite….
    Podrias ayudarme. Estoy super-atascado con este tema y como soy muy principiante con Android estoy un poco desesperado porque es solo la primera pantalla.
    Gracias anticipadas

    • James Revelo

      Hola Jose.

      ¿Tu estás obteniendo el valor en una actividad dentro del evento onItemClickListener()?

      Si es así el objeto debes castearlo al tipo Cursor. Con eso simplemente usas un método get*() como getString() para obtener el valor con el indice de la columna.

      Saludos :D

      • Jose A.

        Esta es la clase que utilizo y que luego llamo desde el MainActivity:
        public void registrarEventos() {
        final ListView lista1 = (ListView) findViewById(R.id.lv1);
        lista1.setOnItemClickListener(new AdapterView.OnItemClickListener(){
        @Override
        public void onItemClick(AdapterView parent, View view, int position, long id) {
        //String itemSeleccionado = AdapterView.getItemAtPosition(position).toString();
        Toast.makeText(getApplicationContext(), “Item ” + lista1.getItemAtPosition(position), Toast.LENGTH_LONG).show();
        }
        });
        }
        y el listview lo lleno con:
        manager = new DataBaseManager(this);
        lista= (ListView) findViewById(R.id.lv1);

        String [] from = new String[]{manager.CN_MATRICULA};
        int[] to = new int[]{android.R.id.text1};

        cursor = manager.cargarCursor();
        adapter = new SimpleCursorAdapter(this, simple_list_item_1,cursor,from,to,0);

        lista.setAdapter(adapter);
        lo que pretendo es seleccionar un elemento del listview y, de momento, mostrarlo, luego poder pasarlo a otra activity, pero no se como mostrarlo.
        Te agradezco la respuesta. Espero me puedas ayudar con este tema.

        • James Revelo

          Amigo getItemAtPositio() retorna en un tipo Object, por lo que tienes que castearlo:

          Cursor c = (Cursor)lista1.getItemAtPosition(position);
          Toast.makeText(getApplicationContext(), “Item ” + c.getString(“dato”), Toast.LENGTH_LONG).show();

          Como te decía, una vez casteado puedes obtener el valor de la columnas con los métodos de la clase Cursor

    • Jose A.

      Este es el codigo que he puesto en una clase que luego llamo desde el MainActivity:
      public void registrarEventos() {
      final ListView lista1 = (ListView) findViewById(R.id.lv1);
      lista1.setOnItemClickListener(new AdapterView.OnItemClickListener(){
      @Override
      public void onItemClick(AdapterView parent, View view, int position, long id) {
      //String itemSeleccionado = AdapterView.getItemAtPosition(position).toString();
      Toast.makeText(getApplicationContext(), “Item ” + lista1.getItemAtPosition(position), Toast.LENGTH_LONG).show();
      }
      });
      }

      el listview lo lleno de esta manera:
      manager = new DataBaseManager(this);
      lista= (ListView) findViewById(R.id.lv1);

      String [] from = new String[]{manager.CN_MATRICULA};
      int[] to = new int[]{android.R.id.text1};

      cursor = manager.cargarCursor();
      adapter = new SimpleCursorAdapter(this, simple_list_item_1,cursor,from,to,0);

      lista.setAdapter(adapter);
      Lo que pretendo es… del listview seleccionar un elemento, y, de momento, que me lo muestre, luego lo querre pasar a otra activity pero si no sé cual es el elmento de esa lista no puedo pasarla.
      Te agradezco la respuesta, haber si me puedes ayudar con este tema.

  • David

    Saludos amigo! Antes de hacerte la consulta permite felicitarte por tu página, es muy buena.
    Ahora bien, mis preguntas son las siguientes: el listView está restringido a TextViews e ImageViews solamente?
    Es decir, qué sucede si lo que deseo inflar es otro formulario más complejo que contengan EditTexts, Spinners, etc.?

    • James Revelo

      Hola DAVID, gracias por tu comentario.

      Bueno si deseas crear un formulario complejo no hay problema amigo. Cada item de la lista será inflado con el layout que tu diseñes.

  • Isaí

    Hola tengo un problema con el ListView, me aparecen 2 veces el mismo registro te lo agradeceria mucho si me ayudaras

    • James Revelo

      Hola Isaí. Esto te sucede con el ejemplo que descargaste o has construido desde cero el proyecto?