# Como exibir uma lista virtual com milhares de itens

> Renderizar listas enormes com VirtualListView + DataAdapter reciclando views (estilo RecyclerView).

Source: https://crossfly.dev/pt/docs/cookbook/lists/lista-virtual-grande — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# Como exibir uma lista virtual com milhares de itens

**Propósito.** Exibir dezenas de milhares de itens sem criar um `Component` por linha. O
`VirtualListView` só cria/renderiza as views visíveis e **recicla** as que saem da tela (como o
`RecyclerView` do Android). Todas as linhas têm **altura fixa** (`Relative itemHeight`).

## APIs usadas
- `new VirtualListView(Relative itemHeight, DataAdapter adapter)` — construtor único; a altura é **fixa** para todas as linhas.
- `interface DataAdapter` (nested em `VirtualListView`), com três métodos:
  - `int getCount()` — total de itens.
  - `Component createView() throws Exception` — cria uma view **em branco**; chamado só quando o pool de reciclagem está vazio.
  - `void bindView(Component view, int index) throws Exception` — popula uma view (nova ou reciclada) com os dados do índice.
- `VirtualListView.notifyDataChanged()` — chame após o dataset crescer/mudar sob **novos** índices (ex.: página anexada). Não rebinda linhas já visíveis.
- `VirtualListView.reloadData()` — recarga total: descarta o cache de views, recicla tudo, rola ao topo e **rebinda** todos os slots visíveis. Use em busca/filtro/ordenação (conteúdo muda sob os mesmos índices).
- `VirtualListView.scrollToIndex(int index)` — rola até centralizar o índice.
- `VirtualListView.getScrollY()` — offset vertical atual em pixels (útil para header colapsável).

## Exemplo completo (copiar, colar e rodar)
```java
package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.graphics.*;   // Relative (para VirtualListView)

/** Tela principal executável. Aponte o launcher (classname) e rode: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      final String[] dados = new String[50_000];
      for (int i = 0; i < dados.length; i++) dados[i] = "Registro " + i;

      Relative alturaItem = Relative.ofFont(3f);   // ~3 alturas de fonte por linha (escala com a UI)

      VirtualListView lista = new VirtualListView(alturaItem, new VirtualListView.DataAdapter()
      {
         public int getCount()
         {
            return dados.length;
         }

         public Component createView() throws Exception
         {
            // view "vazia" reutilizável — SEM dados aqui
            return new Label("");
         }

         public void bindView(Component view, int index) throws Exception
         {
            // popular a view reciclada com os dados do índice atual
            ((Label) view).setText(dados[index]);
         }
      });

      add(LAY_FILL, lista);
   }
}
```

> O trecho abaixo mostra **só o miolo**. Para rodar, coloque-o no esqueleto `MainWindow` acima.

## Trecho mínimo
```java
// verificado na fonte: VirtualListView.java (DataAdapter é interface aninhada)
final String[] dados = new String[50_000];
for (int i = 0; i < dados.length; i++) dados[i] = "Registro " + i;

Relative alturaItem = Relative.ofFont(3f);     // ~3 alturas de fonte por linha (escala com a UI)

VirtualListView lista = new VirtualListView(alturaItem, new VirtualListView.DataAdapter()
{
   public int getCount()
   {
      return dados.length;
   }

   public Component createView() throws Exception
   {
      // view "vazia" reutilizável — SEM dados aqui
      return new Label("");
   }

   public void bindView(Component view, int index) throws Exception
   {
      // popular a view reciclada com os dados do índice atual
      ((Label) view).setText(dados[index]);
   }
});

layout.add(1f, lista);
```

Atualizando os dados:

```java
// Anexei mais itens ao fim (novos índices) → notifyDataChanged
carregarMaisDados(dados);
lista.notifyDataChanged();

// Apliquei um filtro/busca (mesmo índice, conteúdo diferente) → reloadData
aplicarFiltro();
lista.reloadData();   // recicla, volta ao topo e rebinda o visível
```

## Aprofunde-se
- Regra crítica: **nunca** guarde referências às views entre binds — elas são recicladas para outros índices.
- **Linha com vários elementos** (ícone + texto + valor)? Crie um `Component` custom com um método
  `bind(...)` que guarda as sub-referências como campos: `createView()` retorna `new CardTarefa()` e
  `bindView` faz `((CardTarefa) view).bind(dados[index])` (ver [Pensando em componentes](/docs/learn/thinking-in-components)).
- Dados vindos de servidor por página: veja [paginacao.md](/docs/cookbook/lists/paginacao) (usa `PagedDataAdapter`).
- Scroll infinito por append: veja [carregar-mais.md](/docs/cookbook/lists/carregar-mais).
- Guia de widget: [VirtualListView](/docs/ui/widgets/virtuallistview) · Boas práticas: [Performance e listas grandes](/docs/best-practices/performance-e-listas-grandes) · Fonte canônica: Capítulo 15
