# ListView

> Lista desplazable y embebible de ítems de texto, con selección única o múltiple.

Source: https://crossfly.dev/es/docs/ui/widgets/listview — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# ListView

`ListView` es una lista desplazable de ítems de **texto** para selección, lista para embeber en cualquier container. Cada ítem se convierte en un "card" clicable. El modo de selección se fija en el constructor vía `ItemType`: `SINGLE` (tocar selecciona y confirma) o `MULTIPLE` (checkboxes + botón "Confirmar").

A diferencia de `VirtualListView`, el `ListView` **crea un componente por ítem** (sin reciclaje) — es ideal para listas pequeñas/medianas (decenas de ítems), no para miles de filas.

> 🟡 · **Paquete:** `dev.crossfly.ui` · **Extiende:** `Component` · **Estilo:** `ListViewStyle` (ítems/header/botón) + `WindowStyle` (fondo, vía `selectionWindowStyle()`)

## Cuándo usar
- Un selector embebido en una pantalla (menú de opciones, filtros, elección de categoría).
- Pocas decenas de ítems de texto donde la altura exacta sin desplazamiento es útil (`computeIdealHeight`).
- Multi-selección con confirmación explícita (`MULTIPLE`).

## Cuándo NO usar
- Listas grandes (cientos/miles) → usa [`VirtualListView`](/docs/ui/widgets/virtuallistview), que recicla componentes.
- Popup modal de elección → usa [`ListViewWindow`](/docs/ui/widgets/listviewwindow), que envuelve el `ListView` en una `Window`.
- Filas ricas con imagen/varios campos → usa `itemFactory(...)` o monta la lista a mano con [ítems de lista](/docs/ui/widgets/list-items).

## Apariencia
Columna vertical de cards de texto centrados, con encabezado opcional (título + ícono de volver) y, en `MULTIPLE`, un botón "Confirmar" en el pie. En `SINGLE` cada card es un `LabelItem`; en `MULTIPLE`, un `CheckLabelItem` (checkbox + label).

## Ejemplo completo (copiar, pegar y ejecutar)
```java
package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;

/** Pantalla principal ejecutable. Apunta el launcher (classname) y corre: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      ListView list = new ListView(new String[]{"Um", "Dois", "Três"}, ListView.ItemType.SINGLE)
         .showHeader(false);
      list.onConfirm(e -> System.out.println("Escolhido: " + list.getSelectedText()));

      add(LAY_FILL, list);   // las listas usan LAY_FILL (absorben la altura)
   }
}
```

> Los fragmentos de abajo muestran **solo el núcleo** (lo que va dentro de `onShow()`). Para ejecutar, colócalos en el esqueleto `MainWindow` de arriba.

## Uso básico
```java
// SINGLE, sin encabezado, altura automática (sin scroll)
ListView list = new ListView(new String[]{"Um", "Dois", "Três"}, ListView.ItemType.SINGLE)
   .showHeader(false);
list.onConfirm(e -> System.out.println("Escolhido: " + list.getSelectedText()));
contents.add(.95f, list);
```

## Constructores
| Constructor | Descripción |
|---|---|
| `ListView(String[] items, ItemType itemType)` | Lista sin título |
| `ListView(String title, String[] items, ItemType itemType)` | Lista con título en el encabezado |

`ItemType` (definido en `UIConstants`): `SINGLE` o `MULTIPLE`. El modo es **inmutable** tras la construcción.

## Propiedades y personalización
| Propiedad / método | Tipo | Por defecto | Qué hace |
|---|---|---|---|
| `addItem(String)` | `ListView` | — | Agrega un ítem (rebuild perezoso; ignora `null`) |
| `addItems(String...)` | `ListView` | — | Agrega varios ítems (una sola invalidación) |
| `showHeader(boolean)` | `ListView` | `true` | Muestra/oculta el encabezado (título + volver) |
| `title(String)` | `ListView` | `null` | Define el título |
| `confirmText(String)` | `ListView` | `"Confirmar"` | Texto del botón (solo `MULTIPLE`) |
| `itemStyle(ComponentStyle)` | `ListView` | tema | Override de estilo de todos los ítems |
| `confirmButtonStyle(ComponentStyle)` | `ListView` | tema | Estilo del botón de confirmar |
| `headerStyle(LabelStyle)` | `ListView` | tema | Estilo del encabezado |
| `itemFactory(ItemFactory)` | `ListView` | `null` | Fábrica de ítem personalizado (recibe `text`, `index`) |
| `closeAction(Runnable)` | `ListView` | `null` | Acción de cierre (la `ListViewWindow` apunta a `this::hide`) |
| `computeIdealHeight(int width)` | `int` | — | Altura exacta sin desplazamiento, o `-1` si hay `itemFactory` |

### Consulta de selección
`getSelectedCount()`, `getSelectedIndex()`, `getSelectedText()`, `getSelectedIndices()`, `getSelectedTexts()`, `isSelected(int)`, `getSelectionState()` (`boolean[]`).

## Callbacks y eventos
| Callback | Cuándo se dispara |
|---|---|
| `onConfirm(Component.Action)` | `SINGLE`: al tocar un ítem. `MULTIPLE`: al tocar "Confirmar" |

En `SINGLE`, el toque limpia la selección anterior, marca el índice, dispara `onConfirm` y llama a `close()`. En `MULTIPLE`, el toque alterna el checkbox del ítem; la confirmación sale del botón.

## Estados
- **SINGLE** — solo un ítem marcado a la vez; el toque ya confirma y cierra.
- **MULTIPLE** — varios ítems marcables; estado consultable vía `getSelectionState()` incluso antes de confirmar.
- El encabezado y el botón de pie son opcionales (dependen de `showHeader`/`title` y del `ItemType`).

### MULTIPLE con altura automática
```java
ListView multi = new ListView(new String[]{"Vermelho", "Verde", "Azul"},
      ListView.ItemType.MULTIPLE)
   .showHeader(false)
   .confirmText("Aplicar");
multi.getStyle().autoHeight(Relative.ofFont(9f));
multi.onConfirm(e -> {
   for (String t : multi.getSelectedTexts())
      System.out.println("marcado: " + t);
});
contents.add(.95f, multi);
```

### Filas personalizadas con `itemFactory`
```java
ListView list = new ListView(labels, ListView.ItemType.SINGLE).showHeader(false);
list.itemFactory((text, index) -> {
   Component row = new Component();
   row.style(Styles.component().pressable().ingap(Gap.ofFont(.3f)));
   row.setLayout(new GridLayout(Position.START, Position.CENTER));
   row.add(new Label(text));
   return row;
});
list.onConfirm(e -> System.out.println("idx = " + list.getSelectedIndex()));
contents.add(.95f, list);
```

## Buenas prácticas
- Prefiere `addItems(...)` a varios `addItem(...)` — una única invalidación de layout.
- Usa `computeIdealHeight(width)` cuando quieras una lista **sin desplazamiento** con altura exacta (devuelve `-1` si hay `itemFactory`, pues los ítems personalizados no son medibles genéricamente).
- La reconstrucción de los ítems es perezosa (ocurre en el próximo layout), incluso cuando el ancho cambia.

## Errores comunes
- **Usar `ListView` para miles de ítems** — no recicla; la memoria crece linealmente. Migra a [`VirtualListView`](/docs/ui/widgets/virtuallistview).
- **Esperar `onConfirm` en `MULTIPLE` a cada toque** — en `MULTIPLE` el callback solo se dispara en el botón "Confirmar"; consulta `getSelectionState()` para reaccionar a cada marcación.
- **Asumir que existe un constructor sin ítems** — el constructor exige `items` e `ItemType`; comienza con `new String[]{}` y usa `addItems(...)`.

## Ver también
- Guía del área: [`ui/`](/docs/ui) · Recetas: [`cookbook/`](/docs/cookbook)
- Componentes relacionados: [`ListViewWindow`](/docs/ui/widgets/listviewwindow) · [`VirtualListView`](/docs/ui/widgets/virtuallistview) · [ítems de lista (`LabelItem`/`CheckLabelItem`)](/docs/ui/widgets/list-items)
