# Rendimiento y listas grandes

> Mantener 60 fps con muchos datos — virtualización (VirtualListView), paginación y onPaint barato.

Source: https://crossfly.dev/es/docs/best-practices/performance-e-listas-grandes — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# Rendimiento y listas grandes

Cómo mantener 60 fps — incluso en GPU low-end — con muchos datos o dibujo pesado. El "porqué" (paint
thread única, render bajo demanda) está en [La paint thread](/docs/concepts/the-paint-thread) y
[El pipeline de renderizado](/docs/concepts/rendering-pipeline).

## Virtualiza: `VirtualListView`, no `ListView`
`ListView` mantiene **todos** los ítems en memoria. Para cientos/miles, usa `VirtualListView` — él
**recicla vistas** (renderiza solo lo visible). El adapter separa **crear** (una vez, reciclado) de
**vincular** (barato, se ejecuta mucho):
```java
VirtualListView lista = new VirtualListView(Relative.ofFont(3f), new DataAdapter() {
   public int getCount() { return dados.size(); }
   public Component createView() throws Exception {         // creado pocas veces (pool)
      return new Label("");
   }
   public void bindView(Component v, int i) throws Exception {  // corre MUCHO en el scroll → barato e idempotente
      ((Label) v).setText(dados.get(i).nome);
   }
});
// al cambiar los datos:
lista.notifyDataChanged();
```
> `bindView` debe ser **barato e idempotente** — se ejecuta en cada reciclaje al hacer scroll. Nada de I/O,
> asignación ni cálculo caro ahí.

**Por qué reciclar:** el costo real es `createView()`. Reciclando, la memoria queda **constante**
independientemente de `getCount()` — 10 o 10.000 ítems usan el mismo puñado de vistas.

## Paginación remota: `PagedDataAdapter`
Para datos que llegan por página (red), usa `PagedDataAdapter` (caché **LRU** interna):
```java
var adapter = new PagedDataAdapter(50, 4) {   // 50 ítems/página, 4 páginas en caché
   public int getTotalCount() { return total; }
   public void loadPage(int page, PageCallback cb) {
      new Thread(() -> {
         Object[] itens = api.buscarPagina(page, pageSize);   // fuera de la paint thread
         Component.onUI(() -> cb.onPageLoaded(page, itens));   // vuelve a la UI
      }).start();
   }
   public Component createView() throws Exception { return new Label(""); }
   public void bindItem(Component v, Object item, int i) throws Exception {
      ((Label) v).setText(((Cliente) item).nome);
   }
};
```

## Dibujo: `onPaint` barato y render bajo demanda
- **No repintes por frame.** Llama a `repaint()` **solo cuando el estado cambia** — la pantalla no se redibuja
  sola (render bajo demanda).
- **No asignes en `onPaint`.** Se ejecuta para el árbol entero en cada frame; reutiliza objetos (`GRect`,
  `Font`) en campos, no los crees por frame.
- **Nunca bloquees la paint thread** — I/O de base de datos/red en thread de trabajo + `onUI`.

## Buenas prácticas
- `VirtualListView` a partir de ~cientos de ítems; `ListView` solo para listas pequeñas/fijas.
- `bindView`/`bindItem` ligeros; carga imágenes/datos fuera y vincula lo ya-listo.
- Imágenes grandes: redimensiona una vez (escala destructiva) en vez de escalar por frame.

## Errores comunes
- `ListView` con miles de ítems (agota memoria / se traba).
- `bindView` haciendo I/O o asignando (atasca el scroll).
- Repintar en loop/por frame en vez de bajo demanda.

## Ver también
- [VirtualListView](/docs/ui/widgets/virtuallistview) · [PagedDataAdapter](/docs/ui/widgets/paged-data-adapter)
