跳到内容
CrossFly 是 100% 开源在 GitHub 关注我们
CrossFlyCrossFly
开始使用

本页面由葡萄牙语机器翻译,可能存在错误。葡萄牙语版本为准。

如何在虚拟列表中对服务器数据进行分页

目的。 显示一个源为分页(例如:REST API)的大型数据集,而无需一次性加载全部内容。 PagedDataAdapter 维护一个页面的 LRU 缓存:内存中只保留有限数量的页面;当滚动到 数据已被丢弃的区域时,会显示加载指示器并通过 loadPage 重新加载该页面。 在页面加载期间滚动会被锁定。

使用的 API

  • abstract class PagedDataAdapter(int pageSize, int maxCachedPages) — 每页 pageSize 项;内存中最多 maxCachedPages 页(LRU)。实现 VirtualListView.DataAdapter。
  • abstract int getTotalCount() — 服务器上的项目总数(成为列表的 getCount())。
  • abstract void loadPage(int pageIndex, PageCallback callback) — 异步加载一个页面;完成后调用 callback.onPageLoaded(pageIndex, items)。可从任何线程调用——列表会切换到 UI 线程。
  • abstract Component createView() throws Exception — 创建可重用的空白视图。
  • abstract void bindItem(Component view, Object item, int index) throws Exception — 用项目的对象填充视图(而非文本)。与原始的 bindView 不同:这里你接收的是页面已解析好的项。
  • interface PageCallback { void onPageLoaded(int pageIndex, Object[] items); }
  • PagedDataAdapter.isAvailable(int index) — 如果索引已在缓存中则为 true。
  • PagedDataAdapter.clearCache() — 清除缓存和加载状态。

完整示例(复制、粘贴并运行)

package com.exemplo;

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

/** 可执行的主界面。将 launcher 指向此类(classname)并运行:gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   /** 模拟的业务项 — 替换为你真实的实体。 */
   static class Registro
   {
      final String nome;
      Registro(String nome) { this.nome = nome; }
      String getNome() { return nome; }
   }

   static final int totalNoServidor = 12345;   // 例如:API 的 metadata

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

      PagedDataAdapter adapter = new PagedDataAdapter(20, 5)   // 每页 20 项,内存中缓存 5 页
      {
         public int getTotalCount()
         {
            return totalNoServidor;
         }

         public void loadPage(int pageIndex, PageCallback callback)
         {
            // 异步:这里在后台模拟服务器响应
            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("");                  // 可复用的简单行
         }

         public void bindItem(Component view, Object item, int index) throws Exception
         {
            Registro r = (Registro) item;          // 该页中已解析好的项
            ((Label) view).setText(r.getNome());
         }
      };

      // PagedDataAdapter 是一个 DataAdapter:直接传给 VirtualListView
      VirtualListView lista = new VirtualListView(Relative.ofFont(3f), adapter);
      add(LAY_FILL, lista);
   }
}

下面的片段只展示核心部分。要运行它,请将其放入上面的 MainWindow 骨架中。

最小片段

// 已在源码核实:PagedDataAdapter.java
PagedDataAdapter adapter = new PagedDataAdapter(20, 5)   // 每页 20 项,内存中缓存 5 页
{
   public int getTotalCount()
   {
      return totalNoServidor;                // 例如:12345(来自 API 的 metadata)
   }

   public void loadPage(int pageIndex, PageCallback callback)
   {
      // 异步:发起请求并通过回调响应
      api.buscarPagina(pageIndex, 20, registros ->
         callback.onPageLoaded(pageIndex, registros));   // registros = Object[]
   }

   public Component createView() throws Exception
   {
      return new Label("");                  // 可复用的简单行
   }

   public void bindItem(Component view, Object item, int index) throws Exception
   {
      Registro r = (Registro) item;          // 该页中已解析好的项
      ((Label) view).setText(r.getNome());
   }
};

// PagedDataAdapter 是一个 DataAdapter:直接传给 VirtualListView
VirtualListView lista = new VirtualListView(Relative.ofFont(3f), adapter);
layout.add(1f, lista);

深入了解

  • VirtualListView 在构造函数中自动与 adapter 绑定(adapter.listView = this)——无需手动关联。
  • 当 loadPage 正在进行时,adapter 会标记 waitingForData,VirtualListView 会阻止 pan/fling/wheel 并暂停当前的 fling(页面到达时恢复)。
  • 通过追加实现无限滚动(无 LRU 缓存),参见 carregar-mais.md。
  • 组件指南:PagedDataAdapter · 最佳实践:性能与大型列表 · 权威来源:第 15 章
这个页面有帮助吗?