Showing data in a table

IA tooling, not an exam topic. These are JavaFX patterns to adapt for your IA. Nothing here is examinable, and your IA must be your own work.

A TableView shows a list of objects as rows and columns. It ties straight into the OOP you already know: each row is one object of a small model class, and each column reads one of that class’s getters. This page builds the table, fills it, reads the selected row, keeps it current, and filters it live.


Display rows in a scrollable table

What this does: shows your objects as a table with named columns. The table scrolls on its own once there are more rows than fit.

When you would use it in your IA: any list the client needs to see at a glance: products, bookings, students, transactions. The table is the usual home screen for a data-driven app.

The pattern:

First, a small model class. This is ordinary OOP: private fields with getters.

public class Item {
    private String name;
    private int quantity;

    public Item(String name, int quantity) {
        this.name = name;
        this.quantity = quantity;
    }

    public String getName() {
        return name;
    }

    public int getQuantity() {
        return quantity;
    }
}

Controller imports:

import javafx.fxml.FXML;
import javafx.scene.control.TableView;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.PropertyValueFactory;

Controller setup:

@FXML private TableView<Item> tblItems;
@FXML private TableColumn<Item, String> colName;
@FXML private TableColumn<Item, Integer> colQuantity;

@FXML
public void initialize() {
    // The string must match the getter: getName() -> "name", getQuantity() -> "quantity".
    colName.setCellValueFactory(new PropertyValueFactory<>("name"));
    colQuantity.setCellValueFactory(new PropertyValueFactory<>("quantity"));
}

FXML (the table and its columns, with matching fx:ids):

<TableView fx:id="tblItems">
    <columns>
        <TableColumn fx:id="colName" text="Name" />
        <TableColumn fx:id="colQuantity" text="Quantity" />
    </columns>
</TableView>

Make it your own:

  • Replace Item with your own model class, and give it one getter per column you want to show.
  • For each column, the PropertyValueFactory string is the field name as it appears in the getter: getPrice() becomes "price".
  • The <Item> in TableView<Item> is a type parameter that says “this table holds Item objects”. It works like ArrayList<Item>.

Watch out for:

  • If a column comes up blank, the most common cause is a PropertyValueFactory string that does not match the getter name. Check the spelling and capitalisation.
  • The table scrolls automatically, so you do not need to add a scroll bar yourself.

Mix with: Fill a table from your data, Find out which row the user clicked.


Fill a table from your data

What this does: puts your objects into the table by wrapping them in an ObservableList, the special list a TableView watches.

When you would use it in your IA: loading saved records at startup, or showing the contents of an ArrayList you already built elsewhere in the program.

The pattern:

Imports:

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

Controller:

private final ObservableList<Item> items = FXCollections.observableArrayList();

@FXML
public void initialize() {
    // ... column setup from the previous recipe ...

    items.add(new Item("Apples", 12));
    items.add(new Item("Bread", 3));

    tblItems.setItems(items);            // hand the list to the table once
}

If you already have a plain ArrayList, wrap it in one line:

ObservableList<Item> items = FXCollections.observableArrayList(myArrayList);
tblItems.setItems(items);

Make it your own:

  • Build the Item objects from your own data: typed-in values, a loaded file, or a database query.
  • Keep one ObservableList as the table’s data and add to it, rather than building a new list each time.

Watch out for:

  • setItems(...) only needs to be called once. After that, changing the list updates the table for you (see the next recipe).
  • Use FXCollections.observableArrayList(...), not a plain new ArrayList<>(), for the list the table watches. A plain list will display but will not update live.

Mix with: Keep the table up to date, Save and load text.


Find out which row the user clicked

What this does: reads the object for the row the user has selected, so you can edit it, delete it, or show its details.

When you would use it in your IA: an Edit or Delete button that acts on “the row I clicked”; showing a detail panel for the selected record.

The pattern:

@FXML
private void handleEdit() {
    Item selected = tblItems.getSelectionModel().getSelectedItem();
    if (selected == null) {
        // nothing is selected; tell the user and stop
        return;
    }
    // Use selected, for example show its details or open an edit screen.
    openEditScreen(selected);
}

Make it your own:

  • getSelectedItem() returns the full object for that row, so you have every field through its getters.
  • Change openEditScreen(selected) to whatever you want to do with the chosen row.

Watch out for:

  • It returns null when nothing is selected, so always check for null before using the result. Skipping this is the usual cause of a crash on a Delete button.
  • By default the user can select one row at a time, which is what most IA apps want.

Mix with: Ask before doing something destructive, Pass information between screens.


Keep the table up to date

What this does: refreshes the table by changing the ObservableList behind it. Because the table watches that list, the display follows along on its own.

When you would use it in your IA: adding a new record, deleting the selected one, or clearing everything. The table should reflect the change straight away.

The pattern:

// Add a row: it appears immediately.
items.add(new Item("Milk", 2));

// Remove the selected row.
Item selected = tblItems.getSelectionModel().getSelectedItem();
if (selected != null) {
    items.remove(selected);
}

// Clear the whole table.
items.clear();

Make it your own:

  • Add, remove, and clear on the same ObservableList you handed to setItems(...). You do not call setItems again.

Watch out for:

  • This works because you are changing the list itself. If you build a brand-new list, you must call setItems(...) again so the table knows about it.
  • Changing a value inside an existing object (for example raising a quantity in place) does not always refresh the cell, because the table read that value once. Call tblItems.refresh() after such an edit, or remove and re-add the object.

Mix with: Fill a table from your data, Filter the table as the user types, Save and load text.


Filter the table as the user types

What this does: shows only the rows that match what the user types in a search box, updating with every keystroke. It works the same way for a plain list.

When you would use it in your IA: a search box above a long table so the client can find one record quickly without scrolling.

The pattern: keep the full data in one list, and feed the table a shorter list of the rows that match. The match here is a hand-written loop, built on the searching and looping you already know.

Imports:

import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

Controller:

@FXML private TextField txtSearch;

// allItems is the full data; the table shows whichever rows match.
private final ObservableList<Item> allItems = FXCollections.observableArrayList();

@FXML
public void initialize() {
    // ... column setup, then fill allItems ...
    tblItems.setItems(allItems);

    // Re-filter every time the search text changes.
    txtSearch.textProperty().addListener((obs, oldText, newText) -> applyFilter(newText));
}

private void applyFilter(String query) {
    String needle = query.toLowerCase().trim();
    ObservableList<Item> matches = FXCollections.observableArrayList();
    for (Item item : allItems) {
        if (item.getName().toLowerCase().contains(needle)) {
            matches.add(item);
        }
    }
    tblItems.setItems(matches);
}

Make it your own:

  • Change the if condition to match the field your users search by, or to check several fields at once.
  • contains does a “anywhere in the text” match. Use startsWith if you want prefix matching instead.
  • toLowerCase() on both sides makes the search ignore capitalisation.

Watch out for:

  • Keep allItems as the untouched master list and only ever build matches from it. If you filter the master list in place, the rows you hid are gone for good.
  • The listener uses a lambda (the (obs, oldText, newText) -> ... part), which is outside the exam-style Java subset but is the normal way to watch a text field.
  • JavaFX also offers a built-in FilteredList that does this without the manual loop. The loop is shown first because it keeps the algorithm visible; reach for FilteredList once you are comfortable.

Mix with: React to the Enter key, Keep the table up to date.


© EduCS.me — A resource hub for Computer Science education

This site uses Just the Docs, a documentation theme for Jekyll.