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.

ListView

ListView is a scrollable list of text items for selection, ready to embed in any container. Each item becomes a clickable “card”. The selection mode is fixed in the constructor via ItemType: SINGLE (tap selects and confirms) or MULTIPLE (checkboxes + “Confirm” button).

Unlike VirtualListView, ListView creates one component per item (no recycling) — it is ideal for small/medium lists (dozens of items), not for thousands of rows.

🟡 · Package: dev.crossfly.ui · Extends: Component · Style: ListViewStyle (items/header/button) + WindowStyle (background, via selectionWindowStyle())

When to use

  • An embedded selector inside a screen (options menu, filters, category choice).
  • A few dozen text items where exact height without scrolling is useful (computeIdealHeight).
  • Multi-selection with explicit confirmation (MULTIPLE).

When NOT to use

  • Large lists (hundreds/thousands) → use VirtualListView, which recycles components.
  • Modal choice popup → use ListViewWindow, which wraps the ListView in a Window.
  • Rich rows with image/several fields → use itemFactory(...) or build the list by hand with list items.

Appearance

Vertical column of centered text cards, with an optional header (title + back icon) and, in MULTIPLE, a “Confirm” button at the bottom. In SINGLE each card is a LabelItem; in MULTIPLE, a CheckLabelItem (checkbox + label).

Complete example (copy, paste and run)

package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;

/** Runnable main screen. Point the launcher (classname) and run: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      ListView list = new ListView(new String[]{"Um", "Dois", "Três"}, ListView.ItemType.SINGLE)
         .showHeader(false);
      list.onConfirm(e -> System.out.println("Escolhido: " + list.getSelectedText()));

      add(LAY_FILL, list);   // lists use LAY_FILL (they absorb the height)
   }
}

The snippets below show only the core (what goes inside onShow()). To run them, place them in the MainWindow skeleton above.

Basic usage

// SINGLE, no header, automatic height (no scrolling)
ListView list = new ListView(new String[]{"Um", "Dois", "Três"}, ListView.ItemType.SINGLE)
   .showHeader(false);
list.onConfirm(e -> System.out.println("Escolhido: " + list.getSelectedText()));
contents.add(.95f, list);

Constructors

Constructor Description
ListView(String[] items, ItemType itemType) List without title
ListView(String title, String[] items, ItemType itemType) List with title in the header

ItemType (defined in UIConstants): SINGLE or MULTIPLE. The mode is immutable after construction.

Properties and customization

Property / method Type Default What it does
addItem(String) ListView — Adds an item (lazy rebuild; ignores null)
addItems(String...) ListView — Adds several items (a single invalidation)
showHeader(boolean) ListView true Shows/hides the header (title + back)
title(String) ListView null Sets the title
confirmText(String) ListView "Confirmar" Button text (MULTIPLE only)
itemStyle(ComponentStyle) ListView theme Style override for all items
confirmButtonStyle(ComponentStyle) ListView theme Style of the confirm button
headerStyle(LabelStyle) ListView theme Header style
itemFactory(ItemFactory) ListView null Custom item factory (receives text, index)
closeAction(Runnable) ListView null Close action (the ListViewWindow points it to this::hide)
computeIdealHeight(int width) int — Exact height without scrolling, or -1 if there is an itemFactory

Selection queries

getSelectedCount(), getSelectedIndex(), getSelectedText(), getSelectedIndices(), getSelectedTexts(), isSelected(int), getSelectionState() (boolean[]).

Callbacks and events

Callback When it fires
onConfirm(Component.Action) SINGLE: on tapping an item. MULTIPLE: on tapping “Confirm”

In SINGLE, the tap clears the previous selection, marks the index, fires onConfirm and calls close(). In MULTIPLE, the tap toggles the item’s checkbox; confirmation comes from the button.

States

  • SINGLE — only one item marked at a time; the tap already confirms and closes.
  • MULTIPLE — several markable items; state queryable via getSelectionState() even before confirming.
  • Header and footer button are optional (they depend on showHeader/title and the ItemType).

MULTIPLE with automatic height

ListView multi = new ListView(new String[]{"Vermelho", "Verde", "Azul"},
      ListView.ItemType.MULTIPLE)
   .showHeader(false)
   .confirmText("Aplicar");
multi.getStyle().autoHeight(Relative.ofFont(9f));
multi.onConfirm(e -> {
   for (String t : multi.getSelectedTexts())
      System.out.println("marcado: " + t);
});
contents.add(.95f, multi);

Custom rows with itemFactory

ListView list = new ListView(labels, ListView.ItemType.SINGLE).showHeader(false);
list.itemFactory((text, index) -> {
   Component row = new Component();
   row.style(Styles.component().pressable().ingap(Gap.ofFont(.3f)));
   row.setLayout(new GridLayout(Position.START, Position.CENTER));
   row.add(new Label(text));
   return row;
});
list.onConfirm(e -> System.out.println("idx = " + list.getSelectedIndex()));
contents.add(.95f, list);

Best practices

  • Prefer addItems(...) over several addItem(...) calls — a single layout invalidation.
  • Use computeIdealHeight(width) when you want a list without scrolling with exact height (returns -1 if there is an itemFactory, since custom items are not generically measurable).
  • Item rebuilding is lazy (it happens on the next layout), including when the width changes.

Common mistakes

  • Using ListView for thousands of items — it does not recycle; memory grows linearly. Migrate to VirtualListView.
  • Expecting onConfirm in MULTIPLE on every tap — in MULTIPLE the callback only fires on the “Confirm” button; query getSelectionState() to react to each mark.
  • Assuming there is a constructor without items — the constructor requires items and ItemType; start with new String[]{} and use addItems(...).

See also

Was this page helpful?