Skip to content
CrossFly is 100% open sourceFollow us on GitHub
CrossFlyCrossFly
Get started

This page was machine-translated from Portuguese and may contain errors. The Portuguese version is canonical.

Performance and large lists

How to keep 60 fps — even on low-end GPUs — with lots of data or heavy drawing. The “why” (single paint thread, on-demand render) is in The paint thread and The rendering pipeline.

Virtualize: VirtualListView, not ListView

ListView keeps all items in memory. For hundreds/thousands, use VirtualListView — it recycles views (renders only what’s visible). The adapter separates create (once, recycled) from bind (cheap, runs a lot):

VirtualListView lista = new VirtualListView(Relative.ofFont(3f), new DataAdapter() {
   public int getCount() { return dados.size(); }
   public Component createView() throws Exception {         // created only a few times (pool)
      return new Label("");
   }
   public void bindView(Component v, int i) throws Exception {  // runs A LOT on scroll → cheap and idempotent
      ((Label) v).setText(dados.get(i).nome);
   }
});
// when the data changes:
lista.notifyDataChanged();

bindView must be cheap and idempotent — it runs on every recycle during scrolling. No I/O, allocation, or expensive computation there.

Why recycle: the real cost is createView(). By recycling, memory stays constant regardless of getCount() — 10 or 10,000 items use the same handful of views.

Remote pagination: PagedDataAdapter

For data that comes in by page (network), use PagedDataAdapter (internal LRU cache):

var adapter = new PagedDataAdapter(50, 4) {   // 50 items/page, 4 pages cached
   public int getTotalCount() { return total; }
   public void loadPage(int page, PageCallback cb) {
      new Thread(() -> {
         Object[] itens = api.buscarPagina(page, pageSize);   // off the paint thread
         Component.onUI(() -> cb.onPageLoaded(page, itens));   // back to the 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);
   }
};

Drawing: cheap onPaint and on-demand render

  • Don’t repaint every frame. Call repaint() only when the state changes — the screen doesn’t redraw itself (on-demand render).
  • Don’t allocate in onPaint. It runs for the whole tree every frame; reuse objects (GRect, Font) in fields, don’t create them per frame.
  • Never block the paint thread — database/network I/O on a worker thread + onUI.

Best practices

  • VirtualListView from ~hundreds of items on; ListView only for small/fixed lists.
  • Lean bindView/bindItem; load images/data outside and bind the ready-made result.
  • Large images: resize once (destructive scaling) instead of scaling per frame.

Common mistakes

  • ListView with thousands of items (blows up memory / freezes).
  • bindView doing I/O or allocating (stutters the scroll).
  • Repainting in a loop/per frame instead of on demand.

See also

Was this page helpful?