# Performance e listas grandes

> Manter 60 fps com muitos dados — virtualização (VirtualListView), paginação e onPaint barato.

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

# Performance e listas grandes

Como manter 60 fps — inclusive em GPU low-end — com muitos dados ou desenho pesado. O "porquê" (paint
thread única, render sob demanda) está em [A paint thread](/docs/concepts/the-paint-thread) e
[O pipeline de renderização](/docs/concepts/rendering-pipeline).

## Virtualize: `VirtualListView`, não `ListView`
`ListView` mantém **todos** os itens em memória. Para centenas/milhares, use `VirtualListView` — ele
**recicla views** (renderiza só o visível). O adapter separa **criar** (uma vez, reciclado) de
**vincular** (barato, roda muito):
```java
VirtualListView lista = new VirtualListView(Relative.ofFont(3f), new DataAdapter() {
   public int getCount() { return dados.size(); }
   public Component createView() throws Exception {         // criado poucas vezes (pool)
      return new Label("");
   }
   public void bindView(Component v, int i) throws Exception {  // roda MUITO no scroll → barato e idempotente
      ((Label) v).setText(dados.get(i).nome);
   }
});
// ao mudar os dados:
lista.notifyDataChanged();
```
> `bindView` deve ser **barato e idempotente** — ele roda a cada reciclagem no scroll. Nada de I/O,
> alocação ou cálculo caro ali.

**Por quê reciclar:** o custo real é `createView()`. Reciclando, a memória fica **constante**
independentemente de `getCount()` — 10 ou 10.000 itens usam o mesmo punhado de views.

## Paginação remota: `PagedDataAdapter`
Para dados que vêm por página (rede), use `PagedDataAdapter` (cache **LRU** interno):
```java
var adapter = new PagedDataAdapter(50, 4) {   // 50 itens/página, 4 páginas em cache
   public int getTotalCount() { return total; }
   public void loadPage(int page, PageCallback cb) {
      new Thread(() -> {
         Object[] itens = api.buscarPagina(page, pageSize);   // fora da paint thread
         Component.onUI(() -> cb.onPageLoaded(page, itens));   // volta à 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);
   }
};
```

## Desenho: `onPaint` barato e render sob demanda
- **Não repinte por frame.** Chame `repaint()` **só quando o estado muda** — a tela não redesenha
  sozinha (render sob demanda).
- **Não aloque em `onPaint`.** Ele roda para a árvore inteira a cada frame; reuse objetos (`GRect`,
  `Font`) em campos, não crie por frame.
- **Nunca bloqueie a paint thread** — I/O de banco/rede em thread de trabalho + `onUI`.

## Boas práticas
- `VirtualListView` a partir de ~centenas de itens; `ListView` só para listas pequenas/fixas.
- `bindView`/`bindItem` enxutos; carregue imagens/dados fora e vincule o já-pronto.
- Imagens grandes: redimensione uma vez (escala destrutiva) em vez de escalar por frame.

## Erros comuns
- `ListView` com milhares de itens (estoura memória / trava).
- `bindView` fazendo I/O ou alocando (engasga o scroll).
- Repintar em loop/por frame em vez de sob demanda.

## Veja também
- [VirtualListView](/docs/ui/widgets/virtuallistview) · [PagedDataAdapter](/docs/ui/widgets/paged-data-adapter)
