JavaFX TableView Sort Policy


Question

I have a tableview which has an observable list of custom class objects attached to it (Class type: SalesInvoiceNetSale). The data all displays fine within the table. The last item in the observable list is a totals row (Class type: SalesInvoiceNetSaleTotal which extends the SalesInvoiceNetSale class). I simply wish to make my table not sort the last record within the array if the user tries to sort the table by column. I have found another post pretty much asking how to do the same thing but can't seem to figure this out, I suspect it's my non understanding of Java 8's Lambda Expressions. TableView exclude bottom row (total) from sorting

public ObservableList<SalesInvoiceNetSale> applyTableTotalsToSalesInvoiceNetSaleList(ObservableList<SalesInvoiceNetSale> data, TableView table) {

    // Adds A Total Row To The Table View & Disables The Sort Policy 
    double netValueTotal = 0;
    double netDelivery = 0.0;
    double netOversize = 0.0;
    double netDeposit = 0.0;

    for (SalesInvoiceNetSale i : data) {
        netValueTotal += i.getNetValue();
        netDelivery += i.getNetShipping();
        netOversize += i.getNetOversize();
        netDeposit += i.getNetDeposit();
    }

    SalesInvoiceNetSaleTotal rowTotal = new SalesInvoiceNetSaleTotal();
    rowTotal.setNetValue(netValueTotal);
    rowTotal.setNetShipping(netDelivery);
    rowTotal.setNetDeposit(netDeposit);
    rowTotal.setNetOversize(netOversize);
    rowTotal.setLabel("Totals");

    data.add(rowTotal);

    table.sortPolicyProperty().set(t -> {
        Comparator<Row> comparator = (r1, r2)
                -> r1 == TOTAL ? 1 //TOTAL at the bottom
                : r2 == TOTAL ? -1 //TOTAL at the bottom
                : t.getComparator() == null ? 0 //no column sorted: don't change order
                : t.getComparator().compare(r1, r2); //columns are sorted: sort accordingly
        FXCollections.sort(table.getItems(), comparator);
        return true;
    });

    return data;

}

Am very new to JavaFX and can't seem to find by way of example of a sort policy...

1
5
5/23/2017 12:24:02 PM

Accepted Answer

You can try something like this, for your case :

table.sortPolicyProperty().set(t -> {
    Comparator<SalesInvoiceNetSale> comparator = (r1, r2)
            -> r1 == rowTotal ? 1 //rowTotal at the bottom
            : r2 == rowTotal ? -1 //rowTotal at the bottom
            : t.getComparator() == null ? 0 //no column sorted: don't change order
            : t.getComparator().compare(r1, r2); //columns are sorted: sort accordingly
    FXCollections.sort(table.getItems(), comparator);
    return true;
});

Incase you don't understand what is happening here, a snapshot without lambda expression :

table.sortPolicyProperty().set( new Callback<TableView<SalesInvoiceNetSale>, Boolean>() {
    @Override
    public Boolean call(TableView<SalesInvoiceNetSale> param) {
            Comparator<SalesInvoiceNetSale> comparator = new Comparator<SalesInvoiceNetSale>() {
                @Override
                public int compare(SalesInvoiceNetSale r1, SalesInvoiceNetSale r2) {
                if (r1 == rowTotal) {
                    return 1;
                } else if (r2 == rowTotal) {
                    return -1;
                } else if (param.getComparator() == null) {
                    return 0;
                } else {
                    return param.getComparator().compare(r1, r2);
                }
            }
        };
        FXCollections.sort(table.getItems(), comparator);
        return true;
    }
});

Working Example

If you still have doubts, please find a working example, with a scenario similar to yours, I have created a class ExtraPerson which extends Person and made the new object of ExtraPerson as the footer

import java.util.Comparator;

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;

public class TableViewSampleWithoutEdit extends Application {

    private TableView<Person> table = new TableView<Person>();
    private ExtraPerson extraPerson = new ExtraPerson("Ninja Village");

    private final ObservableList<Person> data = FXCollections
            .observableArrayList(
                    new Person("Jacob", "Smith", "jacob.smith@example.com"),
                    new Person("Isabella", "Johnson",
                            "isabella.johnson@example.com"),
                    new Person("Ethan", "Williams",
                            "ethan.williams@example.com"),
                    new Person("Emma", "Jones", "emma.jones@example.com"),
                    new Person("Michael", "Brown", "michael.brown@example.com"),
                    extraPerson);

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        Scene scene = new Scene(new Group());
        stage.setTitle("Table View Sample");
        stage.setWidth(450);
        stage.setHeight(500);

        final Label label = new Label("Address Book");
        label.setFont(new Font("Arial", 20));

        table.setEditable(true);

        TableColumn firstNameCol = new TableColumn("First Name");
        firstNameCol.setMinWidth(100);
        firstNameCol
                .setCellValueFactory(new PropertyValueFactory<Person, String>(
                        "firstName"));

        TableColumn lastNameCol = new TableColumn("Last Name");
        lastNameCol.setMinWidth(100);
        lastNameCol
                .setCellValueFactory(new PropertyValueFactory<Person, String>(
                        "lastName"));

        TableColumn emailCol = new TableColumn("Email");
        emailCol.setMinWidth(200);
        emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>(
                "email"));

        /**
         * Adding comparator to extraPerson
         */

        table.sortPolicyProperty().set(
                new Callback<TableView<Person>, Boolean>() {

                    @Override
                    public Boolean call(TableView<Person> param) {
                        Comparator<Person> comparator = new Comparator<Person>() {
                            @Override
                            public int compare(Person r1, Person r2) {
                                if (r1 == extraPerson) {
                                    return 1;
                                } else if (r2 == extraPerson) {
                                    return -1;
                                } else if (param.getComparator() == null) {
                                    return 0;
                                } else {
                                    return param.getComparator()
                                            .compare(r1, r2);
                                }
                            }
                        };
                        FXCollections.sort(table.getItems(), comparator);
                        return true;
                    }
                });

        table.setItems(data);
        table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);

        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().addAll(label, table);

        ((Group) scene.getRoot()).getChildren().addAll(vbox);

        stage.setScene(scene);
        stage.show();
    }

    public static class Person {

        private final SimpleStringProperty firstName;
        private final SimpleStringProperty lastName;
        private final SimpleStringProperty email;

        private Person(String fName, String lName, String email) {
            this.firstName = new SimpleStringProperty(fName);
            this.lastName = new SimpleStringProperty(lName);
            this.email = new SimpleStringProperty(email);
        }

        public String getFirstName() {
            return firstName.get();
        }

        public void setFirstName(String fName) {
            firstName.set(fName);
        }

        public String getLastName() {
            return lastName.get();
        }

        public void setLastName(String fName) {
            lastName.set(fName);
        }

        public String getEmail() {
            return email.get();
        }

        public void setEmail(String fName) {
            email.set(fName);
        }
    }

    public static class ExtraPerson extends Person {

        private final SimpleStringProperty address;

        private ExtraPerson(String address) {
            super("Itachi", "Uchiha", "leaf@village.ninja");
            this.address = new SimpleStringProperty(address);
        }

        public String getAddress() {
            return address.get();
        }

        public void setAddress(String address) {
            this.address.set(address);
        }

    }
}
9
8/27/2014 8:49:01 AM

Here is the code to do the same thing with a TreeTableView in Java8 and lambda expressions.

treeTable.sortPolicyProperty().set(treeTableView -> {
    Comparator<? super TreeItem<YOURMODEL>> comparator = (model1, model2) -> {

        if(model1.isTotalRow()) {
            return 1;
        } else if(model2.isTotalRow()) {
            return -1;
        } else if (treeTableView.getComparator() == null) {
            return 0;
        } else {
            return treeTableView.getComparator().compare(model1, model2);
        }
    };

    treeTable.getRoot().getChildren().sort(comparator);

    return true;
});

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon