Blog para desarrollo de aplicaciones en Android, aprende paso a paso como crear aplicaciones.

Usamos cookies propias y de terceros que entre otras cosas recogen datos sobre sus hábitos de navegación para mostrarle publicidad personalizada y realizar análisis de uso de nuestro sitio.
Si continúa navegando consideramos que acepta su uso. OK Más información | Y más

ListView y ArrayAdapter en Android

ListView y ArrayAdapter en Android

Hola amigos 👋 Bienvenidos a un nuevo tutorial de Universo Android. Hoy aprenderemos a trabajar con ListView y ArrayAdapter, componentes fundamentales para mostrar listas de datos en Android de forma eficiente y personalizada.

Al finalizar este tutorial tendrás una aplicación que incluirá:

  • ListView básico con ArrayAdapter
  • ListView con datos personalizados
  • Adaptadores personalizados (Custom Adapter)
  • Click listeners en elementos de lista
  • ListView con imágenes y texto
  • Búsqueda y filtrado en ListView
  • Múltiples tipos de vista en lista
  • Actualización dinámica de datos

🟩 1. ¿Qué es ListView y ArrayAdapter?

ListView es un componente que muestra una colección de elementos en una lista vertical desplazable. ArrayAdapter es el puente entre los datos (Array o List) y el ListView, encargándose de convertir cada elemento en una vista.

🟩 2. Diferencia entre ListView y RecyclerView

  • ListView: Más simple, ideal para listas básicas
  • RecyclerView: Más eficiente, flexible y moderno (recomendado para proyectos nuevos)

Nota: Aunque RecyclerView es más moderno, ListView sigue siendo útil para listas simples.

🟩 3. Crear el Proyecto en Android Studio

Creamos un nuevo proyecto en Android Studio con Empty Activity.

🟩 4. Diseño XML con ListView Básico

Crea un archivo llamado:

📄 activity_main.xml

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

    <!-- Título -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ListView con ArrayAdapter"
        android:textSize="24sp"
        android:textStyle="bold"
        android:textColor="#2196F3"
        android:layout_marginBottom="16dp" />

    <!-- Campo de búsqueda -->
    <EditText
        android:id="@+id/txtSearch"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Buscar..."
        android:drawableLeft="@android:drawable/ic_menu_search"
        android:drawablePadding="8dp"
        android:padding="12dp"
        android:background="@drawable/search_background"
        android:layout_marginBottom="16dp" />

    <!-- ListView Principal -->
    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:divider="#CCCCCC"
        android:dividerHeight="1dp"
        android:scrollbars="vertical" />

    <!-- Botón para agregar elementos -->
    <Button
        android:id="@+id/btnAdd"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Agregar Elemento"
        android:layout_marginTop="16dp" />

</LinearLayout>

🟩 5. Crear Drawable para Campo de Búsqueda

Crea res/drawable/search_background.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    
    <solid android:color="#F5F5F5" />
    
    <corners android:radius="8dp" />
    
    <stroke
        android:width="1dp"
        android:color="#CCCCCC" />
    
    <padding
        android:left="12dp"
        android:top="8dp"
        android:right="12dp"
        android:bottom="8dp" />
    
</shape>

🟩 6. Lógica Java con ListView Simple

📄 MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;

public class MainActivity extends AppCompatActivity {

    ListView listView;
    EditText txtSearch;
    Button btnAdd;
    
    ArrayList<String> dataList;
    ArrayAdapter<String> adapter;
    
    int itemCounter = 1;

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

        // Inicializar vistas
        listView = findViewById(R.id.listView);
        txtSearch = findViewById(R.id.txtSearch);
        btnAdd = findViewById(R.id.btnAdd);

        // Crear lista de datos
        createDataList();

        // Configurar ArrayAdapter
        setupAdapter();

        // Configurar listeners
        setupClickListeners();
        
        // Configurar búsqueda
        setupSearch();
    }

    private void createDataList() {
        dataList = new ArrayList<>();
        dataList.add("Java");
        dataList.add("Kotlin");
        dataList.add("Python");
        dataList.add("JavaScript");
        dataList.add("C++");
        dataList.add("C#");
        dataList.add("PHP");
        dataList.add("Swift");
        dataList.add("Go");
        dataList.add("Ruby");
        dataList.add("Rust");
        dataList.add("TypeScript");
    }

    private void setupAdapter() {
        // Crear ArrayAdapter con layout simple predefinido
        adapter = new ArrayAdapter<>(
            this,
            android.R.layout.simple_list_item_1,
            dataList
        );
        
        // Asignar adapter al ListView
        listView.setAdapter(adapter);
    }

    private void setupClickListeners() {
        // Click en elemento de la lista
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                String selectedItem = dataList.get(position);
                Toast.makeText(MainActivity.this, 
                    "Seleccionado: " + selectedItem, 
                    Toast.LENGTH_SHORT).show();
            }
        });

        // Long click en elemento de la lista
        listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                String itemToRemove = dataList.get(position);
                dataList.remove(position);
                adapter.notifyDataSetChanged();
                
                Toast.makeText(MainActivity.this, 
                    "Eliminado: " + itemToRemove, 
                    Toast.LENGTH_SHORT).show();
                
                return true; // Evento consumido
            }
        });

        // Botón agregar
        btnAdd.setOnClickListener(v -> addNewItem());
    }

    private void setupSearch() {
        txtSearch.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                // No se requiere implementación
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                // Filtrar la lista
                adapter.getFilter().filter(s);
            }

            @Override
            public void afterTextChanged(Editable s) {
                // No se requiere implementación
            }
        });
    }

    private void addNewItem() {
        String newItem = "Nuevo Elemento " + itemCounter++;
        dataList.add(0, newItem); // Agregar al inicio
        adapter.notifyDataSetChanged();
        listView.smoothScrollToPosition(0); // Scroll al inicio
        
        Toast.makeText(this, "Elemento agregado", Toast.LENGTH_SHORT).show();
    }
}

🟩 7. Diseño XML para ListView Personalizado

Crea un archivo llamado:

📄 activity_custom_list.xml

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ListView Personalizado"
        android:textSize="24sp"
        android:textStyle="bold"
        android:textColor="#4CAF50"
        android:layout_marginBottom="16dp" />

    <ListView
        android:id="@+id/customListView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="#E0E0E0"
        android:dividerHeight="1dp" />

</LinearLayout>

🟩 8. Crear Layout para Item Personalizado

Crea un archivo llamado:

📄 res/layout/list_item_custom.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="16dp"
    android:background="?android:attr/selectableItemBackground">

    <!-- Imagen circular -->
    <ImageView
        android:id="@+id/imgIcon"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:src="@android:drawable/ic_menu_gallery"
        android:scaleType="centerCrop"
        android:background="@drawable/circular_image" />

    <!-- Contenedor de textos -->
    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical"
        android:layout_marginLeft="16dp"
        android:layout_gravity="center_vertical">

        <!-- Título -->
        <TextView
            android:id="@+id/txtTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Título"
            android:textSize="18sp"
            android:textStyle="bold"
            android:textColor="#000000" />

        <!-- Subtítulo -->
        <TextView
            android:id="@+id/txtSubtitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Subtítulo"
            android:textSize="14sp"
            android:textColor="#666666"
            android:layout_marginTop="4dp" />

        <!-- Descripción -->
        <TextView
            android:id="@+id/txtDescription"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Descripción del elemento"
            android:textSize="12sp"
            android:textColor="#999999"
            android:layout_marginTop="4dp" />

    </LinearLayout>

    <!-- Icono de flecha -->
    <ImageView
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:src="@android:drawable/ic_menu_more"
        android:layout_gravity="center_vertical"
        android:tint="#CCCCCC" />

</LinearLayout>

🟩 9. Crear Drawable para Imagen Circular

Crea res/drawable/circular_image.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    
    <solid android:color="#E0E0E0" />
    
    <size
        android:width="60dp"
        android:height="60dp" />
    
</shape>

🟩 10. Crear Clase de Modelo

📄 ItemModel.java

public class ItemModel {
    private String title;
    private String subtitle;
    private String description;
    private int imageResource;

    public ItemModel(String title, String subtitle, String description, int imageResource) {
        this.title = title;
        this.subtitle = subtitle;
        this.description = description;
        this.imageResource = imageResource;
    }

    // Getters
    public String getTitle() {
        return title;
    }

    public String getSubtitle() {
        return subtitle;
    }

    public String getDescription() {
        return description;
    }

    public int getImageResource() {
        return imageResource;
    }

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

    public void setSubtitle(String subtitle) {
        this.subtitle = subtitle;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public void setImageResource(int imageResource) {
        this.imageResource = imageResource;
    }
}

🟩 11. Crear Adaptador Personalizado

📄 CustomAdapter.java

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;

public class CustomAdapter extends ArrayAdapter<ItemModel> {

    private Context context;
    private ArrayList<ItemModel> items;

    public CustomAdapter(@NonNull Context context, ArrayList<ItemModel> items) {
        super(context, 0, items);
        this.context = context;
        this.items = items;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        
        ViewHolder holder;

        // Patrón ViewHolder para optimizar rendimiento
        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(R.layout.list_item_custom, parent, false);
            
            holder = new ViewHolder();
            holder.imgIcon = convertView.findViewById(R.id.imgIcon);
            holder.txtTitle = convertView.findViewById(R.id.txtTitle);
            holder.txtSubtitle = convertView.findViewById(R.id.txtSubtitle);
            holder.txtDescription = convertView.findViewById(R.id.txtDescription);
            
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        // Obtener el elemento actual
        ItemModel currentItem = items.get(position);

        // Configurar las vistas con los datos
        holder.imgIcon.setImageResource(currentItem.getImageResource());
        holder.txtTitle.setText(currentItem.getTitle());
        holder.txtSubtitle.setText(currentItem.getSubtitle());
        holder.txtDescription.setText(currentItem.getDescription());

        return convertView;
    }

    // ViewHolder para optimizar el rendimiento
    static class ViewHolder {
        ImageView imgIcon;
        TextView txtTitle;
        TextView txtSubtitle;
        TextView txtDescription;
    }
}

🟩 12. Actividad con ListView Personalizado

📄 CustomListActivity.java

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;

public class CustomListActivity extends AppCompatActivity {

    ListView customListView;
    CustomAdapter customAdapter;
    ArrayList<ItemModel> itemList;

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

        customListView = findViewById(R.id.customListView);

        // Crear lista de datos
        createCustomData();

        // Configurar adaptador personalizado
        customAdapter = new CustomAdapter(this, itemList);
        customListView.setAdapter(customAdapter);

        // Configurar click listener
        customListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                ItemModel selectedItem = itemList.get(position);
                Toast.makeText(CustomListActivity.this, 
                    "Seleccionado: " + selectedItem.getTitle(), 
                    Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void createCustomData() {
        itemList = new ArrayList<>();

        itemList.add(new ItemModel(
            "Android Studio",
            "IDE Oficial",
            "Desarrollo de aplicaciones Android",
            android.R.drawable.ic_menu_info_details
        ));

        itemList.add(new ItemModel(
            "Java",
            "Lenguaje de Programación",
            "Lenguaje orientado a objetos",
            android.R.drawable.ic_menu_edit
        ));

        itemList.add(new ItemModel(
            "Kotlin",
            "Lenguaje Moderno",
            "Lenguaje oficial para Android",
            android.R.drawable.ic_menu_manage
        ));

        itemList.add(new ItemModel(
            "Firebase",
            "Backend as a Service",
            "Base de datos en tiempo real",
            android.R.drawable.ic_menu_upload
        ));

        itemList.add(new ItemModel(
            "Material Design",
            "Sistema de Diseño",
            "Guías de diseño de Google",
            android.R.drawable.ic_menu_gallery
        ));

        itemList.add(new ItemModel(
            "Gradle",
            "Sistema de Compilación",
            "Automatización de builds",
            android.R.drawable.ic_menu_preferences
        ));

        itemList.add(new ItemModel(
            "Git",
            "Control de Versiones",
            "Sistema de versionamiento distribuido",
            android.R.drawable.ic_menu_share
        ));

        itemList.add(new ItemModel(
            "XML",
            "Lenguaje de Marcado",
            "Diseño de interfaces en Android",
            android.R.drawable.ic_menu_view
        ));
    }
}

🟩 13. ListView con Diferentes Tipos de Vista

📄 MultiTypeAdapter.java

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;

public class MultiTypeAdapter extends ArrayAdapter<String> {

    private static final int TYPE_HEADER = 0;
    private static final int TYPE_ITEM = 1;
    private static final int TYPE_COUNT = 2;

    private Context context;
    private ArrayList<String> items;

    public MultiTypeAdapter(@NonNull Context context, ArrayList<String> items) {
        super(context, 0, items);
        this.context = context;
        this.items = items;
    }

    @Override
    public int getViewTypeCount() {
        return TYPE_COUNT;
    }

    @Override
    public int getItemViewType(int position) {
        String item = items.get(position);
        return item.startsWith("HEADER:") ? TYPE_HEADER : TYPE_ITEM;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        
        int type = getItemViewType(position);
        
        if (convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(context);
            
            if (type == TYPE_HEADER) {
                convertView = inflater.inflate(android.R.layout.simple_list_item_1, parent, false);
                TextView textView = convertView.findViewById(android.R.id.text1);
                textView.setTextSize(20);
                textView.setTextColor(0xFF2196F3);
                textView.setPadding(16, 24, 16, 8);
            } else {
                convertView = inflater.inflate(android.R.layout.simple_list_item_1, parent, false);
            }
        }

        TextView textView = convertView.findViewById(android.R.id.text1);
        String item = items.get(position);
        
        if (type == TYPE_HEADER) {
            textView.setText(item.replace("HEADER:", ""));
        } else {
            textView.setText(item);
        }

        return convertView;
    }

    @Override
    public boolean isEnabled(int position) {
        // Los headers no son clickeables
        return getItemViewType(position) != TYPE_HEADER;
    }
}

🟩 14. Métodos Útiles de ListView

MétodoDescripción
setAdapter()Asigna un adaptador al ListView
setOnItemClickListener()Detecta clicks en elementos
setOnItemLongClickListener()Detecta long clicks
smoothScrollToPosition()Desplazamiento suave a posición
setSelection()Selecciona un elemento
getSelectedItem()Obtiene el elemento seleccionado
setDivider()Define el divisor entre elementos
setDividerHeight()Altura del divisor
setEmptyView()Vista cuando la lista está vacía

🟩 15. Métodos Útiles de ArrayAdapter

MétodoDescripción
add()Agrega un elemento
addAll()Agrega múltiples elementos
remove()Elimina un elemento
clear()Elimina todos los elementos
notifyDataSetChanged()Notifica cambios en los datos
getFilter()Obtiene el filtro para búsquedas
getCount()Obtiene el número de elementos
getItem()Obtiene un elemento específico

🟩 16. Vista Vacía para ListView

En el XML:

<TextView
    android:id="@+id/emptyView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:text="No hay elementos"
    android:gravity="center"
    android:textSize="18sp"
    android:textColor="#999999" />

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

En Java:

TextView emptyView = findViewById(R.id.emptyView);
listView.setEmptyView(emptyView);

🟩 17. Configurar AndroidManifest.xml

📄 AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AppCompat.Light.DarkActionBar"
        tools:targetApi="31">

        <!-- Actividad Principal -->
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- Actividad de ListView Personalizado -->
        <activity
            android:name=".CustomListActivity"
            android:exported="false"
            android:label="ListView Personalizado" />

    </application>

</manifest>

▶️ Cómo Ejecutar tu Aplicación

  1. Abre Android Studio
  2. Presiona Run para ejecutar el proyecto
  3. Si no tienes un emulador, crea uno desde AVD Manager
  4. Verás el ListView funcionando con búsqueda, agregar y eliminar elementos

🧪 Resultado Final

Tu aplicación mostrará:

  • ListView básico con ArrayAdapter
  • Búsqueda en tiempo real
  • Agregar y eliminar elementos dinámicamente
  • ListView personalizado con imágenes y múltiples textos
  • Click y Long Click listeners funcionando
  • Diferentes tipos de vista en la misma lista
  • Scroll suave y animaciones

📥 Descargar Proyecto de Ejemplo

Puedes descargar el proyecto completo desde el siguiente enlace:

👉 

🙌 Gracias por Visitar mi Blog

Si este tutorial te fue útil:

✔️ Compártelo
✔️ Déjame un comentario
✔️ Sígueme para más contenido sobre Android y programación

¡Estoy aquí para ayudarte!

❓ Preguntas Frecuentes (FAQ)

1. ¿Cuál es la diferencia entre ListView y RecyclerView?
RecyclerView es más eficiente y flexible, reutiliza vistas automáticamente con ViewHolder obligatorio. ListView es más simple pero menos eficiente para listas grandes. RecyclerView es la opción recomendada para proyectos nuevos.

2. ¿Qué es el patrón ViewHolder y por qué es importante?
ViewHolder es un patrón que guarda las referencias a las vistas de cada elemento, evitando llamadas repetidas a findViewById(), lo que mejora significativamente el rendimiento del scroll en listas largas.

3. ¿Cómo puedo agregar búsqueda en mi ListView?
Usa el método getFilter() del ArrayAdapter junto con TextWatcher en un EditText. El filtro integrado de ArrayAdapter permite búsquedas automáticas en la lista sin código adicional complejo.

4. ¿Puedo tener diferentes tipos de elementos en el mismo ListView?
Sí, sobrescribe los métodos getViewTypeCount() y getItemViewType() en tu adaptador personalizado para definir múltiples tipos de vista y retornar layouts diferentes según el tipo de elemento.

No hay comentarios:

Publicar un comentario