Christian Rädel
Christian Rädel

Reputation: 591

JavaFx - TableView: How to use custom Comparator<T> for sorting?

... where T is the generic type of the TableView.

I'm implementing a file-listview with three columns, so far. Each of type java.nio.file.Path. For the name column, I wrote a Comparator<Path> which sorts the files with directories-first and case-insensitiv. The other two comparators sort by last-modified-time and file-size. For this they compare on long fields.

But the comparatorProperty of a column is based on Comparator<String>. Which, I think, sorts based on the displayed text...

So I have to find a way, to use the sort-on-header-click feature with the type of the TableView?

Upvotes: 2

Views: 2745

Answers (2)

codeDr
codeDr

Reputation: 1694

While @Christian Rädel answer was correct, it didn't show how to implement it.

I was looking for this implementation because I wanted to sort the columns by a 'sort value' and not the 'displayed value.'

For instance, the Name field is preferred to be displayed as 'FirstName LastName', but you'd like to have the field sorted by 'LastName, FirstName'. Also, you might have a column that is composite as 'TagName [version]' and you'd prefer the version to be sorted numerically instead of alphabetically.

public class SomeView extends SplitPane
{   
    ToolBar searchBar = new ToolBar();
    TextField searchField = new TextField();

    TableView<Widget> widgets = new TableView<Widget>();

    TableColumn<Widget, String> number_col;
    TableColumn<Widget, WidgetTitle> title_col;
    TableColumn<Widget, WidgetName> name_col;
    TableColumn<Widget, WidgetVersion> version_col;


    public SomeView() 
    {   
        super();

        setOrientation(Orientation.VERTICAL);
        setDividerPositions(0.05);

        searchField.setPrefWidth(200);
        searchField.setMaxWidth(360);
        searchField.setMinWidth(80);
        searchField.setPrefColumnCount(360);
        searchField.setText("");

        searchBar.getItems().add(searchField);

        number_col.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Widget, String>, ObservableValue<String>>() {
            @Override public ObservableValue<String> call(TableColumn.CellDataFeatures<Widget, String> p) {
                return new ReadOnlyObjectWrapper( (1 + widgets.getItems().indexOf(p.getValue())) + "");
            }
        });
        number_col.setSortable(false);
        number_col.setPrefWidth(60);
        number_col.setMinWidth(30);

        title_col.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Widget, WidgetTitle>, ObservableValue<WidgetTitle>>() {
            public ObservableValue<WidgetTitle> call(TableColumn.CellDataFeatures<Widget, WidgetTitle> p) {
                return new ReadOnlyObjectWrapper<WidgetTitle>( new WidgetTitle(p.getValue()) );
            }
        });
    
        name_col.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Widget, WidgetName>, ObservableValue<WidgetName>>() {
            public ObservableValue<WidgetName> call(TableColumn.CellDataFeatures<Widget, WidgetName> p) {
                return new ReadOnlyObjectWrapper<WidgetName>( new WidgetName(p.getValue()) );
            }
        });
        version_col.setMinWidth(80.0);
        version_col.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Widget, WidgetVersion>, ObservableValue<WidgetVersion>>() {
            public ObservableValue<WidgetVersion> call(TableColumn.CellDataFeatures<Widget, WidgetVersion> p) {     
                return new ReadOnlyObjectWrapper<WidgetVersion>( new WidgetVersion(p.getValue()) );
            }
        });
    
       widgets.getColumns().setAll(number_col, title_col, name_col, version_col, date_col);

       ObservableList<Widget> widgetList = widgets.geItems();
       widgetList.addAll( /* get List<Widget> */ );

       FilteredList<Widget> filtered = new FilteredList<>(widgetList);
        searchField.textProperty().addListener((obsv, ovalue, nvalue) -> {
            filtered.setPredicate( awidget -> {
                if (null == nvalue || nvalue.isEmpty())
                    return true;
                String lcfilter = nvalue.toLowerCase();
                if (awidget.getName().toLowerCase().contains(lcfilter)
                    || awidget.getTitle().toLowerCase().contains(lcfilter))
                    return true;
               return false;
           });
       });

        SortedList<Widget> sorted = new SortedList<>(filtered);
        widgets.setItems(sorted);
        sorted.comparatorProperty().bind(widgets.comparatorProperty());

        getItems().addAll(searchBar, widgets);
    }

    // customize the sorting of Title Column
    private class WidgetTitle implements Comparable<WidgetTitle>
    {
        Widget widget;

        WidgetTitle(Widget title)
        {
            widget = title;
        }

        @Override
        public int compareTo(WidgetTitle obj)
        {
            return widget.getSortTitle().compareTo(obj.widget.getSortTitle());
        }

        @Override
        public String toString()
        {
            return widget.getTitle();
        }
    }

    // customize the sorting of Name Column
    private class WidgetName implements Comparable<WidgetName>
    {   
        Widget widget;

        WidgetName(Widget name)
        {
            widget = name;
        }

        @Override
        public int compareTo(WidgetName obj)
        {
            return widget.getNameSort().compareTo(obj.widget.getNameSort());
        }

        @Override
        public String toString()
        {
            return widget.getName();
        }
    }

    // customize the sorting of Version Column
    private class WidgetVersion implements Comparable<WidgetVersion>
    {
        Widget widget;

        WidgetVersion(Widget version)
        {
            widget = version;
        }

        @Override
        public int compareTo(WidgetVersion obj)
        {
            String ser1 = widget.getVersionName();
            String ser2 = obj.widget.getVersionName();

            if (null == ser1 && null != ser2)
                return -1;

            if (null != ser1 && null == ser2)
                return 1;

            int scompare = ser1.compareTo(ser2);
            return (0 == scompare)
                ? widget.getVersionIndex().compareTo(obj.widget.getVersionIndex()) : scompare;
        }

        @Override
        public String toString()
        {
            return widget.versionProperty().getValue();
        }
    }
}

Upvotes: 0

Christian R&#228;del
Christian R&#228;del

Reputation: 591

You use wrong type parameters, when creating your TableColumn. If you create a TableColumn<Path, Path>, you can specify a Comparator<Path> for that column. Likewise TableColumn<Path, FileTime> and TableColumn<Path, Long> to use Comparator<FileTime> and Comparator<Long>.

From the docs:

Class TableColumnBase<S,T>
Type Parameters:
S - The type of the UI control (e.g. the type of the 'row').
T - The type of the content in all cells in this table column.

Upvotes: 2

Related Questions