Como paginar dados de servidor em uma lista virtual
Propósito. Exibir um conjunto de dados grande cuja fonte é paginada (ex.: API REST) sem
carregar tudo de uma vez. O PagedDataAdapter mantém um cache LRU de páginas: só um número
limitado de páginas fica em memória; ao rolar para uma região cujos dados foram descartados, um
spinner aparece e a página é recarregada via loadPage. O scroll é travado enquanto a página
carrega.
APIs usadas
abstract class PagedDataAdapter(int pageSize, int maxCachedPages)—pageSizeitens por página; no máximomaxCachedPagespáginas em memória (LRU). ImplementaVirtualListView.DataAdapter.abstract int getTotalCount()— total de itens no servidor (vira ogetCount()da lista).abstract void loadPage(int pageIndex, PageCallback callback)— carrega uma página de forma assíncrona; ao terminar, chamecallback.onPageLoaded(pageIndex, items). Pode ser chamado de qualquer thread — a lista faz o switch para a UI.abstract Component createView() throws Exception— cria a view em branco reutilizável.abstract void bindItem(Component view, Object item, int index) throws Exception— popula a view com o objeto do item (não o texto). Diferente dobindViewcru: aqui você recebe o item já resolvido pela página.interface PageCallback { void onPageLoaded(int pageIndex, Object[] items); }PagedDataAdapter.isAvailable(int index)— true se o índice já está no cache.PagedDataAdapter.clearCache()— limpa cache e estado de loading.
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
{
/** Item de negócio simulado — troque pela sua entidade real. */
static class Registro
{
final String nome;
Registro(String nome) { this.nome = nome; }
String getNome() { return nome; }
}
static final int totalNoServidor = 12345; // ex.: metadata da API
@Override
public void onShow() throws Exception
{
super.onShow();
setLayout(new VerticalLayout());
PagedDataAdapter adapter = new PagedDataAdapter(20, 5) // 20 itens/página, 5 páginas em memória
{
public int getTotalCount()
{
return totalNoServidor;
}
public void loadPage(int pageIndex, PageCallback callback)
{
// assíncrono: aqui simulamos a resposta do servidor em background
onUI(() ->
{
Object[] registros = new Object[20];
for (int i = 0; i < registros.length; i++)
registros[i] = new Registro("Item " + (pageIndex * 20 + i));
callback.onPageLoaded(pageIndex, registros);
});
}
public Component createView() throws Exception
{
return new Label(""); // linha simples reutilizável
}
public void bindItem(Component view, Object item, int index) throws Exception
{
Registro r = (Registro) item; // o item já resolvido da página
((Label) view).setText(r.getNome());
}
};
// PagedDataAdapter é um DataAdapter: passe direto ao VirtualListView
VirtualListView lista = new VirtualListView(Relative.ofFont(3f), adapter);
add(LAY_FILL, lista);
}
}
O trecho abaixo mostra só o miolo. Para rodar, coloque-o no esqueleto
MainWindowacima.
Trecho mínimo
// verificado na fonte: PagedDataAdapter.java
PagedDataAdapter adapter = new PagedDataAdapter(20, 5) // 20 itens/página, 5 páginas em memória
{
public int getTotalCount()
{
return totalNoServidor; // ex.: 12345 (vindo de metadata da API)
}
public void loadPage(int pageIndex, PageCallback callback)
{
// assíncrono: dispara a requisição e responde pelo callback
api.buscarPagina(pageIndex, 20, registros ->
callback.onPageLoaded(pageIndex, registros)); // registros = Object[]
}
public Component createView() throws Exception
{
return new Label(""); // linha simples reutilizável
}
public void bindItem(Component view, Object item, int index) throws Exception
{
Registro r = (Registro) item; // o item já resolvido da página
((Label) view).setText(r.getNome());
}
};
// PagedDataAdapter é um DataAdapter: passe direto ao VirtualListView
VirtualListView lista = new VirtualListView(Relative.ofFont(3f), adapter);
layout.add(1f, lista);
Aprofunde-se
- O
VirtualListViewliga-se ao adapter automaticamente no construtor (adapter.listView = this) — não é preciso amarrar manualmente. - Enquanto
loadPageestá em voo, o adapter marcawaitingForDatae oVirtualListViewbloqueia pan/fling/wheel e pausa o fling atual (retomado ao chegar a página). - Para scroll infinito por append (sem cache LRU), veja carregar-mais.md.
- Guia de widget: PagedDataAdapter · Boas práticas: Performance e listas grandes · Fonte canônica: Capítulo 15
Esta página foi útil?Obrigado pelo feedback!