Saltar al contenido
CrossFly es 100% open sourceSíguenos en GitHub
CrossFlyCrossFly
Empezar

Esta página fue traducida automáticamente del portugués y puede contener errores. La versión en portugués es la canónica.

Cómo paginar datos de servidor en una lista virtual

Propósito. Mostrar un conjunto de datos grande cuya fuente está paginada (ej.: API REST) sin cargar todo de una vez. El PagedDataAdapter mantiene una caché LRU de páginas: solo un número limitado de páginas queda en memoria; al desplazarse a una región cuyos datos fueron descartados, un spinner aparece y la página se recarga mediante loadPage. El scroll se bloquea mientras la página carga.

APIs usadas

  • abstract class PagedDataAdapter(int pageSize, int maxCachedPages) — pageSize elementos por página; como máximo maxCachedPages páginas en memoria (LRU). Implementa VirtualListView.DataAdapter.
  • abstract int getTotalCount() — total de elementos en el servidor (se convierte en el getCount() de la lista).
  • abstract void loadPage(int pageIndex, PageCallback callback) — carga una página de forma asíncrona; al terminar, llama a callback.onPageLoaded(pageIndex, items). Puede ser llamado desde cualquier thread — la lista hace el switch a la UI.
  • abstract Component createView() throws Exception — crea la view en blanco reutilizable.
  • abstract void bindItem(Component view, Object item, int index) throws Exception — puebla la view con el objeto del elemento (no el texto). A diferencia del bindView crudo: aquí recibes el elemento ya resuelto por la página.
  • interface PageCallback { void onPageLoaded(int pageIndex, Object[] items); }
  • PagedDataAdapter.isAvailable(int index) — true si el índice ya está en la caché.
  • PagedDataAdapter.clearCache() — limpia caché y estado de loading.

Ejemplo completo (copiar, pegar y ejecutar)

package com.exemplo;

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

/** Pantalla principal ejecutable. Apunta el launcher (classname) y corre: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   /** Ítem de negocio simulado — cámbialo por tu entidad real. */
   static class Registro
   {
      final String nome;
      Registro(String nome) { this.nome = nome; }
      String getNome() { return nome; }
   }

   static final int totalNoServidor = 12345;   // ej.: metadata de la API

   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      PagedDataAdapter adapter = new PagedDataAdapter(20, 5)   // 20 ítems/página, 5 páginas en memoria
      {
         public int getTotalCount()
         {
            return totalNoServidor;
         }

         public void loadPage(int pageIndex, PageCallback callback)
         {
            // asíncrono: aquí simulamos la respuesta del servidor en 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("");                  // fila simple reutilizable
         }

         public void bindItem(Component view, Object item, int index) throws Exception
         {
            Registro r = (Registro) item;          // el ítem ya resuelto de la página
            ((Label) view).setText(r.getNome());
         }
      };

      // PagedDataAdapter es un DataAdapter: pásalo directo al VirtualListView
      VirtualListView lista = new VirtualListView(Relative.ofFont(3f), adapter);
      add(LAY_FILL, lista);
   }
}

El fragmento de abajo muestra solo el núcleo. Para ejecutarlo, colócalo en el esqueleto MainWindow de arriba.

Fragmento mínimo

// verificado en la fuente: PagedDataAdapter.java
PagedDataAdapter adapter = new PagedDataAdapter(20, 5)   // 20 ítems/página, 5 páginas en memoria
{
   public int getTotalCount()
   {
      return totalNoServidor;                // ej.: 12345 (viniendo de metadata de la API)
   }

   public void loadPage(int pageIndex, PageCallback callback)
   {
      // asíncrono: dispara la petición y responde por el callback
      api.buscarPagina(pageIndex, 20, registros ->
         callback.onPageLoaded(pageIndex, registros));   // registros = Object[]
   }

   public Component createView() throws Exception
   {
      return new Label("");                  // fila simple reutilizable
   }

   public void bindItem(Component view, Object item, int index) throws Exception
   {
      Registro r = (Registro) item;          // el ítem ya resuelto de la página
      ((Label) view).setText(r.getNome());
   }
};

// PagedDataAdapter es un DataAdapter: pásalo directo al VirtualListView
VirtualListView lista = new VirtualListView(Relative.ofFont(3f), adapter);
layout.add(1f, lista);

Profundiza

  • El VirtualListView se enlaza al adapter automáticamente en el constructor (adapter.listView = this) — no hace falta amarrarlo manualmente.
  • Mientras loadPage está en vuelo, el adapter marca waitingForData y el VirtualListView bloquea pan/fling/wheel y pausa el fling actual (retomado al llegar la página).
  • Para scroll infinito por append (sin caché LRU), mira carregar-mais.md.
  • Guía del widget: PagedDataAdapter · Buenas prácticas: Rendimiento y listas grandes · Fuente canónica: Capítulo 15
¿Te resultó útil esta página?