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.

How to implement load-more (infinite scroll)

Purpose. Automatically load more data as the user approaches the end of the list, appending items to the existing dataset (append). Unlike pagination with an LRU cache (paginacao.md), here you control the dataset and the list only signals when it is time to fetch more.

APIs used

  • VirtualListView.onLoadMore(Runnable callback) — fires when the scroll gets near the end. Only one trigger stays pending at a time (loadMorePending), preventing duplicate calls.
  • VirtualListView.loadMoreThreshold(int threshold) — how many items before the end fire the callback. Default 5.
  • VirtualListView.notifyDataChanged() — required to call after appending the new items; it clears loadMorePending, hides the spinner, and re-measures the content.

Complete example (copy, paste and run)

package com.exemplo;

import java.util.*;   // List / ArrayList

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

/** Runnable main screen. Point the launcher (classname) and run: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   final List<String> dados = new ArrayList<>();
   int proxima = 0;

   /** Appends a batch of 30 items to the dataset (simulates a server page). */
   private void carregarPagina()
   {
      for (int i = 0; i < 30; i++)
         dados.add("Registro " + (proxima++));
   }

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

      carregarPagina();   // populates the first batch

      VirtualListView lista = new VirtualListView(Relative.ofFont(3f), new VirtualListView.DataAdapter()
      {
         public int getCount()                    { return dados.size(); }
         public Component createView()            { return new Label(""); }
         public void bindView(Component v, int i) throws Exception
         {
            ((Label) v).setText(dados.get(i));
         }
      });

      lista.loadMoreThreshold(8);   // starts fetching 8 items before the end (default is 5)
      lista.onLoadMore(() ->
      {
         // fires ONCE when nearing the end; further fires only after notifyDataChanged
         onUI(() ->
         {
            carregarPagina();             // APPENDS (new indices at the end)
            lista.notifyDataChanged();    // releases the next load-more and hides the spinner
         });
      });

      add(LAY_FILL, lista);
   }
}

The snippet below shows only the core. To run it, place it inside the MainWindow skeleton above.

Minimal snippet

// verified in source: VirtualListView.java (onLoadMore / loadMoreThreshold / notifyDataChanged)
final Array<String> dados = new Array<>();
carregarPagina(dados, 0);   // populates the first batch

VirtualListView lista = new VirtualListView(Relative.ofFont(3f), new VirtualListView.DataAdapter()
{
   public int getCount()                    { return dados.size(); }
   public Component createView()            { return new Label(""); }
   public void bindView(Component v, int i) throws Exception
   {
      ((Label) v).setText(dados.get(i));
   }
});

lista.loadMoreThreshold(8);   // starts fetching 8 items before the end (default is 5)
lista.onLoadMore(() ->
{
   // fires ONCE when nearing the end; further fires only after notifyDataChanged
   api.proximaPagina(novos ->
   {
      dados.addAll(novos);          // APPENDS (new indices at the end)
      lista.notifyDataChanged();    // releases the next load-more and hides the spinner
   });
});

layout.add(1f, lista);

Go deeper

  • The VirtualListView automatically shows a spinner in the footer while the load-more is pending (controlled by VirtualListViewStyle.showSpinner).
  • If you do not call notifyDataChanged(), loadMorePending stays stuck at true and the callback never fires again.
  • Need disposable pages with a bounded cache (instead of infinite append)? Use paginacao.md.
  • Widget guide: VirtualListView · Sibling recipe: lista-virtual-grande.md · Canonical source: Chapter 15
Was this page helpful?