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 emVirtualListView), 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)
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
MainWindowacima.
Trecho mínimo
// 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:
// 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
Componentcustom com um métodobind(...)que guarda as sub-referências como campos:createView()retornanew CardTarefa()ebindViewfaz((CardTarefa) view).bind(dados[index])(ver Pensando em componentes). - Dados vindos de servidor por página: veja paginacao.md (usa
PagedDataAdapter). - Scroll infinito por append: veja carregar-mais.md.
- Guia de widget: VirtualListView · Boas práticas: Performance e listas grandes · Fonte canônica: Capítulo 15
Esta página foi útil?Obrigado pelo feedback!