Descripción General
Este artículo aborda el uso de componentes de interfaz de usuario en Android, enfocándose en listas desplegables (Spinner), campos de autocompletado (AutoCompleteTextView) y listas (ListView). Se incluyen ejemplos para escuchar eventos de selección, vincular datos dinámicamente, gestionar selecciones múltiples, menejar pulsaciones largas y utilizar adaptadores.
Estructura Principal
Se definen cuatro botones en el archivo de diseño principal, cada uno redirige a una actividad diferente para demostrar funcionalidades específicas.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btnDesplegable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Lista desplegable"/>
<Button
android:id="@+id/btnAutocompletar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Autocompletado"/>
<Button
android:id="@+id/btnListaMultiple"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Lista con selección"/>
<Button
android:id="@+id/btnListaPersonalizada"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Lista detallada"/>
</LinearLayout>
Actividad Principal
La actividad principal implementa un listener para manejar clics en los botones y navegar a las actividades correspondientes.
package com.example.ejemploandroid;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity implements View.OnClickListener {
private Button botonDesplegable;
private Button botonAutocompletar;
private Button botonListaMultiple;
private Button botonListaPersonalizada;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
botonDesplegable = findViewById(R.id.btnDesplegable);
botonDesplegable.setOnClickListener(this);
botonAutocompletar = findViewById(R.id.btnAutocompletar);
botonAutocompletar.setOnClickListener(this);
botonListaMultiple = findViewById(R.id.btnListaMultiple);
botonListaMultiple.setOnClickListener(this);
botonListaPersonalizada = findViewById(R.id.btnListaPersonalizada);
botonListaPersonalizada.setOnClickListener(this);
}
@Override
public void onClick(View vista) {
Intent intentNavegacion = null;
switch (vista.getId()) {
case R.id.btnDesplegable:
intentNavegacion = new Intent(this, DesplegableActivity.class);
break;
case R.id.btnAutocompletar:
intentNavegacion = new Intent(this, AutocompletadoActivity.class);
break;
case R.id.btnListaMultiple:
intentNavegacion = new Intent(this, ListaSeleccionActivity.class);
break;
case R.id.btnListaPersonalizada:
intentNavegacion = new Intent(this, ListaDetalladaActivity.class);
break;
}
if (intentNavegacion != null) {
startActivity(intentNavegacion);
}
}
}
Manejo de Listas Desplegables (Spinner)
Se configuran dos Spinner: uno con datos estáticos de un recurso de cadenas y otro con datos vinculados dinámicamente. Se implementan oyentes para detectar cambios en la selección.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:id="@+id/activityDesplegable"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Elige una ciudad"
android:textSize="24sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Spinner
android:id="@+id/spinnerCiudades"
android:entries="@array/nombresCiudades"
android:spinnerMode="dialog"
android:prompt="@string/tituloSeleccion"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="@android:color/holo_blue_light"/>
<TextView
android:text="Selecciona otra ciudad"
android:textSize="24sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Spinner
android:id="@+id/spinnerAlternativo"
android:spinnerMode="dialog"
android:prompt="@string/tituloSeleccion"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
En el archivo de recursos de cadenas (strings.xml), se definen las ciudades:
<resources>
<string name="app_name">EjemploUI</string>
<string name="tituloSeleccion">Selecciona ciudad</string>
<array name="nombresCiudades">
<item>Lima</item>
<item>Bogotá</item>
<item>Santiago</item>
<item>México DF</item>
<item>Buenos Aires</item>
<item>Caracas</item>
</array>
</resources>
La actividad correspondiente escucha eventos y vincula datos dinámicamente.
package com.example.ejemploandroid;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.Toast;
public class DesplegableActivity extends Activity {
private Spinner selectorPrincipal;
private Spinner selectorDinamico;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_desplegable);
selectorPrincipal = findViewById(R.id.spinnerCiudades);
selectorPrincipal.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adaptador, View vista, int posicion, long id) {
String[] listaCiudades = getResources().getStringArray(R.array.nombresCiudades);
Toast.makeText(DesplegableActivity.this, "Elegido: " + listaCiudades[posicion], Toast.LENGTH_SHORT).show();
}
@Override
public void onNothingSelected(AdapterView<?> adaptador) {}
});
selectorDinamico = findViewById(R.id.spinnerAlternativo);
String[] nuevasCiudades = {"Quito", "Montevideo", "La Paz", "Asunción"};
ArrayAdapter adaptadorLocal = new ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, nuevasCiudades);
selectorDinamico.setAdapter(adaptadorLocal);
}
}
Autocompletado de Datos
Se utiliza AutoCompleteTextView para sugerir opciones basadas en la entrada del usuario, vinculando un adaptador con datos de ejemplo.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activityAutocompletar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<AutoCompleteTextView
android:id="@+id/campoAutocompletar"
android:completionHint="Escribe para buscar"
android:completionThreshold="1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
La actividad vincula el adaptador al control.
package com.example.ejemploandroid;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
public class AutocompletadoActivity extends Activity {
private AutoCompleteTextView campoEntrada;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_autocompletar);
campoEntrada = findViewById(R.id.campoAutocompletar);
String[] sugerencias = {"manzana", "mango", "melocotón", "mandarina", "melón"};
ArrayAdapter adaptadorSugerencias = new ArrayAdapter(this, android.R.layout.simple_list_item_1, sugerencias);
campoEntrada.setAdapter(adaptadorSugerencias);
}
}
Selección Múltiple y Eventos en ListView
Se demuestran ListView con selección múltiple, eventos de clic y pulsación larga, y funcionalidades para seleccionar todos o invertir la selección. Además, se muestran ejemplos de eliminación por pulsación larga.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activityListaSeleccion"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="5dp"
android:orientation="vertical">
<ListView
android:id="@+id/listaEstatica"
android:entries="@array/nombresCiudades"
android:divider="@android:color/holo_blue_bright"
android:dividerHeight="1dp"
android:listSelector="@android:color/holo_green_light"
android:choiceMode="multipleChoice"
android:layout_width="match_parent"
android:layout_height="200dp"/>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="@android:color/darker_gray"/>
<ListView
android:id="@+id/listaDinamica"
android:divider="@android:color/holo_blue_bright"
android:dividerHeight="1dp"
android:choiceMode="multipleChoice"
android:listSelector="@android:color/holo_green_light"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:layout_width="match_parent"
android:layout_weight="1"
android:onClick="toggleSeleccion"
android:text="Invertir selección"
android:layout_height="wrap_content" />
<Button
android:layout_width="match_parent"
android:layout_weight="1"
android:onClick="obtenerValores"
android:text="Obtener valores"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
La actividad maneja eventos, vincula datos dinámicamente y permite operaciones de selección.
package com.example.ejemploandroid;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class ListaSeleccionActivity extends Activity {
private ListView vistaListaEstatica;
private ListView vistaListaDinamica;
private ArrayAdapter adaptadorLista;
private List elementosDatos;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lista_seleccion);
vistaListaEstatica = findViewById(R.id.listaEstatica);
final String[] datosCiudades = getResources().getStringArray(R.array.nombresCiudades);
vistaListaEstatica.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adaptador, View vista, int posicion, long id) {
Toast.makeText(ListaSeleccionActivity.this, datosCiudades[posicion], Toast.LENGTH_SHORT).show();
}
});
vistaListaEstatica.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> adaptador, View vista, int posicion, long id) {
Toast.makeText(ListaSeleccionActivity.this, "Pulsación larga en: " + datosCiudades[posicion], Toast.LENGTH_SHORT).show();
return true;
}
});
vistaListaDinamica = findViewById(R.id.listaDinamica);
elementosDatos = new ArrayList();
elementosDatos.add("Ana");elementosDatos.add("Luis");
elementosDatos.add("María");elementosDatos.add("Pedro");
adaptadorLista = new ArrayAdapter(this, android.R.layout.simple_list_item_multiple_choice, elementosDatos);
vistaListaDinamica.setAdapter(adaptadorLista);
vistaListaDinamica.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> adaptador, View vista, int posicion, long id) {
elementosDatos.remove(posicion);
adaptadorLista.notifyDataSetChanged();
return false;
}
});
}
public void toggleSeleccion(View vista) {
int totalSeleccionados = vistaListaDinamica.getCheckedItemCount();
boolean nuevoEstado = totalSeleccionados == 0;
for (int i = 0; i < vistaListaDinamica.getCount(); i++) {
vistaListaDinamica.setItemChecked(i, nuevoEstado);
}
}
public void obtenerValores(View vista) {
StringBuilder acumulador = new StringBuilder();
for (int i = 0; i < vistaListaDinamica.getCount(); i++) {
if (vistaListaDinamica.isItemChecked(i)) {
acumulador.append(vistaListaDinamica.getItemAtPosition(i)).append(", ");
}
}
Toast.makeText(this, acumulador.toString(), Toast.LENGTH_LONG).show();
}
}
ListView con Contenido Personalizado
Se utiliza SimpleAdapter para mostrar una lista con elementos complejos, como imágenes y texto, vinculando datos desde una colección de mapas.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activityListaDetallada"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="@+id/listaUsuarios"
android:divider="@android:color/holo_purple"
android:dividerHeight="1dp"
android:choiceMode="multipleChoice"
android:listSelector="@android:color/holo_green_light"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
La actividad prepara datos y configura el adaptador.
package com.example.ejemploandroid;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ListaDetalladaActivity extends Activity {
private ListView vistaLista;
private SimpleAdapter adaptadorComplejo;
private List<Map<String, Object>> coleccionDatos;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lista_detallada);
vistaLista = findViewById(R.id.listaUsuarios);
String[] claves = {"imagenUsuario", "nombreUsuario", "detalleUsuario"};
int[] vistas = {R.id.imagenUsuario, R.id.nombreUsuario, R.id.detalleUsuario};
cargarDatos();
adaptadorComplejo = new SimpleAdapter(this, coleccionDatos, R.layout.elemento_lista_usuario, claves, vistas);
vistaLista.setAdapter(adaptadorComplejo);
}
private void cargarDatos() {
coleccionDatos = new ArrayList<Map<String, Object>>();
Map<String, Object> registro = new HashMap<String, Object>();
registro.put("imagenUsuario", R.drawable.usuario1);
registro.put("nombreUsuario", "Carlos");
registro.put("detalleUsuario", "Desarrollador Android");
coleccionDatos.add(registro);
registro = new HashMap<String, Object>();
registro.put("imagenUsuario", R.drawable.usuario2);
registro.put("nombreUsuario", "Laura");
registro.put("detalleUsuario", "Diseñadora UI");
coleccionDatos.add(registro);
registro = new HashMap<String, Object>();
registro.put("imagenUsuario", R.drawable.usuario2);
registro.put("nombreUsuario", "Roberto");
registro.put("detalleUsuario", "Gerente de proyectos");
coleccionDatos.add(registro);
}
}
El diseño del elemento de lista incluye una imagen, nombre y descripción.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_marginLeft="10dp">
<ImageView
android:id="@+id/imagenUsuario"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_gravity="center_vertical"/>
<LinearLayout
android:orientation="vertical"
android:layout_marginLeft="15dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical">
<TextView
android:id="@+id/nombreUsuario"
android:textSize="22sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/detalleUsuario"
android:textSize="18sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>