ListView Text Alignment

The ListView class of JavaFX 8 shows one item per line – one String in the simplest case. What if you want to visually separate individual string fragments (words, numbers)? You could use a TableView with multiple columns, but that may not be appropriate for your data. Or you could insert tab characters ("\t") into each string, but tab spacing is not configurable and rather unpredictable.

A better way to align individual parts of a single text column is to implement your own ListCell class. Assume a cell data type of ItemData with three properties left, middle, right that should appear at the corresponding positions within each column, with the right part also right-aligned. Here’s what the ListCell implementation would look like:

class CustomListCell extends ListCell<ItemData> {

    private Font _itemFont = ...;

    @Override
    protected void updateItem(ItemData item, boolean empty) {
        super.updateItem(item, empty);

        Pane pane = null;
        if (!empty) {
            pane = new Pane();

            // left-aligned text at position 0em
            final Text leftText = new Text(item.left());
            leftText.setFont(_itemFont);
            leftText.setTextOrigin(VPos.TOP);
            leftText.relocate(0, 0);

            // left-aligned text at position 4em 
            final Text middleText = new Text(item.middle());
            middleText.setFont(_itemFont);
            middleText.setTextOrigin(VPos.TOP);
            final double em = middleText.getLayoutBounds().getHeight();
            middleText.relocate(4 * em, 0);

            // right-aligned text at position 8em
            final Text rightText = new Text(item.right());
            rightText.setFont(_itemFont);
            rightText.setTextOrigin(VPos.TOP);
            final double width = rightText.getLayoutBounds().getWidth();
            rightText.relocate(8 * em - width, 0);

            pane.getChildren().addAll(leftText, middleText, rightText);
        }

        setText("");
        setGraphic(pane);
    }
}

The sample assumes you want to specify a custom text font, here stored in the field _itemFont. We must set this font for each Text object before we start measuring and positioning the object. That’s especially important if you rely on CSS styling for fonts. Such styling is applied after the layout calculations in updateItem have been performed, so you’ll get the correct font but wrong alignments if you don’t also set the font in code before taking metrics.

Even though we want to show plain text, we need to use setGraphic to enable precise alignment. We put all Text objects in a basic Pane that allows absolute positioning. The first Text is simply left-aligned at (0, 0). The second Text is also left-aligned but should appear at a “column” position of 4em, based on our custom font, so that’s what we specify in relocate. And the third Text should be right-aligned, so we measure its width and subtract it from the desired “column” position of 8em.

Note that you must call both setText and setGraphic in every updateItem call, even if just to clear them. That’s because ListView optimizes item display by reusing existing ListCell instances. If you don’t set both display properties, the current instance continues to show the previous item’s values!

All that remains is enabling the new ListCell styling, and that’s easy with the new lambda syntax: myListView.setCellFactory(t -> new CustomListCell()); If you only have single-line items as in the code sample, you might also wish to call setFixedCellSize with e.g. 1.1em of your item font. And that’s how you align text fragments in a ListView item.

3 thoughts on “ListView Text Alignment”

  1. This worked for me…thanks so much! One issue is that I was not able to figure out how to setStyle for -fx-text-fill; somehow, when I switched the item datatype from String to my composite type (two String properties), using the above method, this single style setting for text fill stopped working. Other style settings work fine. As a workaround, I just changed the font to bold and set the background to a different color.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.