本页面由葡萄牙语机器翻译,可能存在错误。葡萄牙语版本为准。
PagedDataAdapter
PagedDataAdapter 是面向 VirtualListView 的分页 DataAdapter,带页面 LRU 缓存。它只在内存中保留若干页面;当滚动到某个页面已被丢弃的区域时,会显示加载指示器并通过 loadPage 异步重新加载。
它是一个抽象类:你需要实现 getTotalCount、loadPage、createView 和 bindItem。索引→页面的转换、缓存、加载指示器以及加载期间的滚动锁定都已现成提供。
🔴 · 包:
dev.crossfly.ui· 实现:VirtualListView.DataAdapter· 用法: 传给VirtualListView的构造函数
何时使用
- 数据来自网络/数据库并按页获取,无法全部放入内存。
- 你希望自动缓存(LRU),并在回到旧区域时透明地重新加载。
何时不要使用
- 所有数据已在内存中 → 直接在
VirtualListView上实现DataAdapter。 - 小型列表 →
ListView。
完整示例(复制、粘贴并运行)
package com.exemplo;
import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.graphics.*; // Relative
/** 可执行的主界面。将 launcher 指向此类(classname)并运行: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 项,共 5 页
public int getTotalCount() { return total; }
public void loadPage(int pageIndex, PageCallback cb) {
// 模拟一次加载(请替换为后台的网络/数据库调用)。
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); // 列表使用 LAY_FILL(占满高度)
}
}
下面的片段只展示核心部分(放在
onShow()里的内容)。要运行它们,请放入上面的MainWindow骨架中。
基本用法
PagedDataAdapter adapter = new PagedDataAdapter(20, 5) { // 每页 20 项,内存中缓存 5 页
public int getTotalCount() { return serverTotal; }
public void loadPage(int pageIndex, PageCallback cb) {
api.fetch(pageIndex, data -> 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);
layout.add(LAY_FILL, list);
构造函数
| 构造函数 | 描述 |
|---|---|
PagedDataAdapter(int pageSize, int maxCachedPages) |
pageSize:每页项数。maxCachedPages:内存中保留的页面数(LRU) |
这两个字段都是 public final。当你把此 adapter 传给 VirtualListView 的构造函数时,VirtualListView 会将自身注入到该 adapter 中。
需要实现的抽象方法
| 方法 | 描述 |
|---|---|
int getTotalCount() |
服务器上的项总数(由 getCount() 使用) |
void loadPage(int pageIndex, PageCallback callback) |
异步加载一页;完成时调用 callback.onPageLoaded(pageIndex, items)(可在任意线程) |
Component createView() throws Exception |
创建一个空白视图 |
void bindItem(Component view, Object item, int index) throws Exception |
将原始项(Object)绑定到视图 —— 请转换为你的类型 |
有用的属性与方法
| 方法 | 类型 | 作用 |
|---|---|---|
isAvailable(int index) |
boolean |
该项是否已在缓存中 |
clearCache() |
void |
清空缓存和加载状态 |
getCount() |
int |
委托给 getTotalCount() |
工作原理(概要)
bindView(view, index)转换出pageIndex = index / pageSize和itemInPage = index % pageSize。- 页面在缓存中 → 立即
bindItem(waitingForData = false)。 - 页面缺失 →
waitingForData = true并触发requestPage(pageIndex)。
- 页面在缓存中 → 立即
requestPage避免重复请求(loading),显示加载指示器并暂停 fling;回调返回时,用Component.onUI(...)编组到 UI 线程,写入页面,隐藏加载指示器,强制重新绑定该范围(invalidateRange)并恢复 fling。cache是一个处于访问顺序(access-order)模式的LinkedHashMap,当超过maxCachedPages时丢弃最旧的页面(LRU)。
回调与事件
| 回调 | 触发时机 |
|---|---|
PageCallback.onPageLoaded(int pageIndex, Object[] items) |
你在完成页面加载时调用(可在任意线程) |
状态
waitingForData—— 只要有页面在加载就为true;向VirtualListView发出信号以锁定滚动。- LRU —— 旧页面被丢弃;滚动回去时会重新加载(新的加载指示器 + 请求)。
示例 —— 类型化项的转换
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; // 对原始 Object 进行 cast
((Label) view).setText(c.getNome());
}
};
VirtualListView list = new VirtualListView(Relative.ofFont(2.4f), adapter);
最佳实践
- 页面中的项以
Object[]到达 —— 在bindItem中将item转换为你的类型。 - 在回调之前不要在
loadPage内调用 UI API —— adapter 在收到onPageLoaded时已在内部切换到 UI 线程。 - 根据滚动模式调整
maxCachedPages:更大的缓存可避免回滚时重新加载,代价是更多内存。
常见错误
- 用错误的
pageIndex调用onPageLoaded—— adapter 用该索引写入缓存并使正确的范围失效。 - 在
loadPage中阻塞 —— 它必须是异步的;UI 线程不能等待网络。
另请参阅
- 领域指南:
ui/· 实用配方:cookbook/ - 相关组件:
VirtualListView
这个页面有帮助吗?感谢你的反馈!