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

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

如何显示包含数千项的虚拟列表

目的。 显示数万个项目而无需为每一行创建一个 Component。 VirtualListView 只创建/渲染可见的视图,并回收移出屏幕的视图(就像 Android 的 RecyclerView)。所有行都具有固定高度(Relative itemHeight)。

使用的 API

  • new VirtualListView(Relative itemHeight, DataAdapter adapter) — 唯一构造函数;所有行的高度都是固定的。
  • interface DataAdapter(嵌套在 VirtualListView 中),包含三个方法:
    • int getCount() — 项目总数。
    • Component createView() throws Exception — 创建一个空白视图;仅在回收池为空时调用。
    • void bindView(Component view, int index) throws Exception — 用索引处的数据填充视图(新建或回收的)。
  • VirtualListView.notifyDataChanged() — 在数据集在新索引下增长/变化后调用(例如:追加了一页)。不会重新绑定已可见的行。
  • VirtualListView.reloadData() — 完全重载:丢弃视图缓存、回收所有内容、滚动到顶部并重新绑定所有可见的槽位。用于搜索/过滤/排序(内容在相同索引下发生变化)。
  • VirtualListView.scrollToIndex(int index) — 滚动至使索引居中。
  • VirtualListView.getScrollY() — 当前垂直偏移量(以像素为单位,对可折叠头部很有用)。

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

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
{
   @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 倍字体高度(随 UI 缩放)

      VirtualListView lista = new VirtualListView(alturaItem, new VirtualListView.DataAdapter()
      {
         public int getCount()
         {
            return dados.length;
         }

         public Component createView() throws Exception
         {
            // 可复用的 "空" view — 这里没有数据
            return new Label("");
         }

         public void bindView(Component view, int index) throws Exception
         {
            // 用当前索引的数据填充回收的 view
            ((Label) view).setText(dados[index]);
         }
      });

      add(LAY_FILL, lista);
   }
}

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

最小片段

// 已在源码核实:VirtualListView.java(DataAdapter 是嵌套接口)
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 倍字体高度(随 UI 缩放)

VirtualListView lista = new VirtualListView(alturaItem, new VirtualListView.DataAdapter()
{
   public int getCount()
   {
      return dados.length;
   }

   public Component createView() throws Exception
   {
      // 可复用的 "空" view — 这里没有数据
      return new Label("");
   }

   public void bindView(Component view, int index) throws Exception
   {
      // 用当前索引的数据填充回收的 view
      ((Label) view).setText(dados[index]);
   }
});

layout.add(1f, lista);

更新数据:

// 我向末尾追加了更多项(新索引)→ notifyDataChanged
carregarMaisDados(dados);
lista.notifyDataChanged();

// 我应用了筛选/搜索(相同索引,内容不同)→ reloadData
aplicarFiltro();
lista.reloadData();   // 回收,回到顶部并重新绑定可见项

深入了解

  • 关键规则:绝不要在多次绑定之间保存对视图的引用——它们会被回收用于其他索引。
  • 一行包含多个元素(图标 + 文本 + 数值)?创建一个自定义 Component,带一个 bind(...) 方法,将子引用作为字段保存:createView() 返回 new CardTarefa(),而 bindView 执行 ((CardTarefa) view).bind(dados[index])(参见 以组件方式思考)。
  • 数据按页从服务器获取:参见 paginacao.md(使用 PagedDataAdapter)。
  • 通过追加实现无限滚动:参见 carregar-mais.md。
  • 组件指南:VirtualListView · 最佳实践:性能与大型列表 · 权威来源:第 15 章
这个页面有帮助吗?