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étodo | Descripció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étodo | Descripció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
- Abre Android Studio
- Presiona Run para ejecutar el proyecto
- Si no tienes un emulador, crea uno desde AVD Manager
- 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