colomb
colomb

Reputation: 318

JavaFX TableView sorting consistency on property update

I have a TableView with data changing asynchronously. The TableView has a sort and I'd like updated rows to be sorted according to the current sort. New rows added to the model are sorted correctly, but changing the data doesn't get reflected in the current sort.

Is the only solution to call TableView.sort() after each update to the data?

import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class SortingItOut extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        ObservableList<Person> data = FXCollections.observableArrayList();
        Person c = new Person("C", 120);
        data.addAll(new Person("A", 100), new Person("B", 50), c, new Person("D", 25));

        TableColumn<Person, String> nameColumn = new TableColumn<>("Name");
        nameColumn.setCellValueFactory(param -> param.getValue().name);
        TableColumn<Person, Number> ageColumn = new TableColumn<>("Age");
        ageColumn.setCellValueFactory(param -> param.getValue().age);

        TableView<Person> table = new TableView<>(data);
        table.getColumns().addAll(nameColumn, ageColumn);
        table.getSortOrder().add(ageColumn);

        SortedList<Person> sortedList = new SortedList<>(data);
        sortedList.comparatorProperty().bind(table.comparatorProperty());

        table.setItems(sortedList);

        Button modify = new Button("modify C age");
        modify.setOnAction(e -> c.setAge(c.getAge() == 120 ? 75 : 120));

        Button add = new Button("add");
        add.setOnAction(e -> data.add(new Person("E", (int) (Math.random() * 100))));

        VBox box = new VBox(10);
        box.setAlignment(Pos.CENTER);

        box.getChildren().addAll(table, modify, add);
        Scene scene = new Scene(box);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public class Person {

        private final SimpleStringProperty name = new SimpleStringProperty("");
        private final SimpleIntegerProperty age = new SimpleIntegerProperty(0);

        public Person(String name, Integer age) {
            setName(name);
            setAge(age);
        }

        public String getName() {
            return name.get();
        }

        public void setName(String name) {
            this.name.set(name);
        }

        public void setAge(Integer age) {
            this.age.set(age);
        }

        public Integer getAge() {
            return age.get();
        }
    }

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

Upvotes: 2

Views: 1774

Answers (2)

James_D
James_D

Reputation: 209269

The table cannot re-sort automatically when you change the values in existing elements, because you haven't provided any mechanism for the SortedList to "know" when those changes occur.

If you expose the JavaFX properties in your model class:

public class Person {

    private final StringProperty name = new SimpleStringProperty("");
    private final IntegerProperty age = new SimpleIntegerProperty(0);

    public Person(String name, Integer age) {
        setName(name);
        setAge(age);
    }

    public StringProperty nameProperty() {
        return name ;
    }

    public IntegerProperty ageProperty() {
        return age ;
    }

    public String getName() {
        return name.get();
    }

    public void setName(String name) {
        this.name.set(name);
    }

    public void setAge(Integer age) {
        this.age.set(age);
    }

    public Integer getAge() {
        return age.get();
    }
}

and create your underlying list with an extractor so that it fires events when the properties of interest change:

ObservableList<Person> data = FXCollections.observableArrayList(p -> 
    new Observable[] {p.nameProperty(), p.ageProperty()});

then the SortedList will receive events when the properties change, and will be able to reorder the elements "automatically" as required.

Upvotes: 1

colomb
colomb

Reputation: 318

As pointed out by @Enigo, this solution will ensure the table has correct sorting after updates to properties within each of the list items.

Upvotes: 0

Related Questions