Pular para o conteúdo
CrossFly é 100% open sourceSiga no GitHub
CrossFlyCrossFly
Começar

PagedDataAdapter

PagedDataAdapter é um DataAdapter paginado para VirtualListView, com um cache LRU de páginas. Mantém só algumas páginas em memória; ao rolar para uma região cuja página foi descartada, mostra um spinner e recarrega assincronamente via loadPage.

É uma classe abstrata: você implementa getTotalCount, loadPage, createView e bindItem. A tradução índice→página, o cache, o spinner e a trava de scroll durante o carregamento já vêm prontos.

🔴 · Pacote: dev.crossfly.ui · Implementa: VirtualListView.DataAdapter · Uso: passado ao construtor de VirtualListView

Quando usar

  • Dados que vêm de rede/banco em páginas e não cabem todos em memória.
  • Você quer cache automático (LRU) com recarga transparente ao voltar a uma região antiga.

Quando NÃO usar

  • Todos os dados já estão em memória → implemente DataAdapter diretamente na VirtualListView.
  • Lista pequena → ListView.

Exemplo completo (copiar, colar e rodar)

package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.graphics.*;   // Relative

/** 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 int pageSize = 20;
      final int total    = 10_000;

      PagedDataAdapter adapter = new PagedDataAdapter(pageSize, 5) { // 20 itens/página, 5 páginas
         public int getTotalCount() { return total; }
         public void loadPage(int pageIndex, PageCallback cb) {
            // Simula um carregamento (troque por chamada de rede/banco em background).
            Object[] data = new Object[pageSize];
            for (int i = 0; i < pageSize; i++)
               data[i] = "Item " + (pageIndex * pageSize + i);
            cb.onPageLoaded(pageIndex, data);
         }
         public Component createView() { return new Label(""); }
         public void bindItem(Component view, Object item, int index) {
            ((Label) view).setText(item.toString());
         }
      };

      VirtualListView list = new VirtualListView(Relative.ofFont(2f), adapter);

      add(LAY_FILL, list);   // listas usam LAY_FILL (absorvem a altura)
   }
}

Os trechos abaixo mostram só o miolo (o que vai dentro de onShow()). Para rodar, coloque-os no esqueleto MainWindow acima.

Uso básico

PagedDataAdapter adapter = new PagedDataAdapter(20, 5) { // 20 itens/página, 5 páginas em memória
   public int getTotalCount() { return serverTotal; }
   public void loadPage(int pageIndex, PageCallback cb) {
      api.fetch(pageIndex, data -> cb.onPageLoaded(pageIndex, data)); // pode ser background
   }
   public Component createView() { return new Label(""); }
   public void bindItem(Component view, Object item, int index) {
      ((Label) view).setText(item.toString());
   }
};
VirtualListView list = new VirtualListView(Relative.ofFont(2f), adapter);
layout.add(LAY_FILL, list);

Construtores

Construtor Descrição
PagedDataAdapter(int pageSize, int maxCachedPages) pageSize: itens por página. maxCachedPages: páginas mantidas em memória (LRU)

Ambos os campos são public final. A VirtualListView injeta a si mesma no adapter quando você passa este adapter ao seu construtor.

Métodos abstratos a implementar

Método Descrição
int getTotalCount() Total de itens no servidor (usado por getCount())
void loadPage(int pageIndex, PageCallback callback) Carrega uma página assíncrona; chame callback.onPageLoaded(pageIndex, items) ao terminar (de qualquer thread)
Component createView() throws Exception Cria uma view em branco
void bindItem(Component view, Object item, int index) throws Exception Vincula o item bruto (Object) à view — faça o cast para o seu tipo

Propriedades e métodos úteis

Método Tipo O que faz
isAvailable(int index) boolean Se o item já está no cache
clearCache() void Zera cache e estado de carregamento
getCount() int Delega a getTotalCount()

Funcionamento (resumo)

  • bindView(view, index) traduz pageIndex = index / pageSize e itemInPage = index % pageSize.
    • Página no cache → bindItem imediato (waitingForData = false).
    • Página ausente → waitingForData = true e dispara requestPage(pageIndex).
  • requestPage evita requisições duplicadas (loading), mostra spinner e pausa o fling; ao voltar o callback, marshala para a thread de UI com Component.onUI(...), grava a página, esconde o spinner, força re-bind da faixa (invalidateRange) e retoma o fling.
  • O cache é um LinkedHashMap em modo access-order que descarta a página mais antiga (LRU) quando excede maxCachedPages.

Callbacks e eventos

Callback Quando dispara
PageCallback.onPageLoaded(int pageIndex, Object[] items) Você chama ao terminar de carregar a página (de qualquer thread)

Estados

  • waitingForData — true enquanto alguma página carrega; sinaliza à VirtualListView para travar o scroll.
  • LRU — páginas antigas são descartadas; ao rolar de volta, são recarregadas (novo spinner + requisição).

Exemplo — cast de item tipado

PagedDataAdapter adapter = new PagedDataAdapter(50, 4) {
   public int getTotalCount() { return repo.total(); }
   public void loadPage(int page, PageCallback cb) {
      repo.loadAsync(page, 50, lista -> cb.onPageLoaded(page, lista.toArray()));
   }
   public Component createView() { return new Label(""); }
   public void bindItem(Component view, Object item, int index) {
      Cliente c = (Cliente) item;            // cast do Object bruto
      ((Label) view).setText(c.getNome());
   }
};
VirtualListView list = new VirtualListView(Relative.ofFont(2.4f), adapter);

Boas práticas

  • Os itens da página chegam como Object[] — faça o cast do item para o seu tipo em bindItem.
  • Não chame APIs de UI dentro de loadPage antes do callback — o adapter já troca para a thread de UI internamente ao receber onPageLoaded.
  • Ajuste maxCachedPages ao padrão de rolagem: cache maior evita recargas ao voltar, ao custo de memória.

Erros comuns

  • Chamar onPageLoaded com pageIndex errado — o adapter usa esse índice para gravar no cache e invalidar a faixa correta.
  • Bloquear em loadPage — deve ser assíncrono; a thread de UI não pode esperar a rede.

Veja também

Esta página foi útil?