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

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

性能与大列表

如何保持 60 fps——即使在低端 GPU 上——同时面对大量数据或繁重的绘制。其中的“为什么”(单一绘制线程、按需渲染)请见 绘制线程 和 渲染管线。

虚拟化:用 VirtualListView,而非 ListView

ListView 会把所有项目保留在内存中。对于成百上千项,请使用 VirtualListView——它会回收视图(只渲染可见部分)。适配器将创建(一次性,被回收复用)与绑定(开销小,运行很频繁)分离开来:

VirtualListView lista = new VirtualListView(Relative.ofFont(3f), new DataAdapter() {
   public int getCount() { return dados.size(); }
   public Component createView() throws Exception {         // 很少创建(pool)
      return new Label("");
   }
   public void bindView(Component v, int i) throws Exception {  // 滚动时频繁运行 → 廉价且幂等
      ((Label) v).setText(dados.get(i).nome);
   }
});
// 数据变化时:
lista.notifyDataChanged();

bindView 必须开销小且幂等——它在滚动时每次回收都会运行。那里不要做任何 I/O、分配或昂贵的计算。

为什么要回收: 真正的成本在于 createView()。通过回收,内存保持恒定,与 getCount() 无关——10 项还是 10,000 项,都只用同一小把视图。

远程分页:PagedDataAdapter

对于按页(网络)到来的数据,使用 PagedDataAdapter(内部有 LRU 缓存):

var adapter = new PagedDataAdapter(50, 4) {   // 每页 50 项,缓存 4 页
   public int getTotalCount() { return total; }
   public void loadPage(int page, PageCallback cb) {
      new Thread(() -> {
         Object[] itens = api.buscarPagina(page, pageSize);   // 不在 paint thread 中
         Component.onUI(() -> cb.onPageLoaded(page, itens));   // 回到 UI
      }).start();
   }
   public Component createView() throws Exception { return new Label(""); }
   public void bindItem(Component v, Object item, int i) throws Exception {
      ((Label) v).setText(((Cliente) item).nome);
   }
};

绘制:低开销的 onPaint 与按需渲染

  • 不要每帧重绘。 只在状态变化时调用 repaint()——屏幕不会自行重绘(按需渲染)。
  • 不要在 onPaint 中分配对象。 它会为整棵树在每一帧运行;把对象(GRect、Font)复用在字段里,不要每帧新建。
  • 绝不要阻塞绘制线程——数据库/网络 I/O 放在工作线程 + onUI。

最佳实践

  • 从约数百项起用 VirtualListView;ListView 只用于小型/固定列表。
  • bindView/bindItem 保持精简;在外部加载图片/数据,再绑定已就绪的内容。
  • 大图片:一次性缩放(破坏性缩放),而不是每帧缩放。

常见错误

  • 用 ListView 装载数千项(内存溢出/卡死)。
  • bindView 做 I/O 或分配(导致滚动卡顿)。
  • 循环/逐帧重绘,而不是按需重绘。

另请参阅

这个页面有帮助吗?