mynameisJEFF
mynameisJEFF

Reputation: 4239

Javafx: Detect ALL changes made to tableView including addition/deletion of table rows and cell edits on any table rows

Given that I have a tableView, how can I track all the changes, i.e. a new row is added / deleted, one of the table cells has its value changed, and fire off the same event when any of these changes are detected ?

At the moment, I have the following code below, which only manages to detect change if I add in a row. It is not able to detect changes when I edit a table cell of any row and when I delete a row.

    ObservableList<Trade> observableListOfTrades =FXCollections.observableArrayList();


    observableListOfTrades.add(newTrade);  
fxTransactionLog.getItems().add(observableListOfTrades.get(observableListOfTrades.size()-1));


        observableListOfTrades.addListener(new ListChangeListener() {
            @Override
            public void onChanged(ListChangeListener.Change change) {
                System.out.println("Detected a change! ");
            }
        });

As requested, I am posting my Trade Class

public class Trade{

// properties
private ObjectProperty<LocalDate> transactionDate;
private StringProperty itemName;
private StringProperty buySell;
private DoubleProperty volume;
private DoubleProperty price;
private DoubleBinding transactionFee;


public Trade(BuySell buySell, LocalDate transactionDate, double volume, double price){
    this.buySell = new SimpleStringProperty(buySell.toString());
    this.itemName = new SimpleStringProperty("testing");
    this.transactionDate = new SimpleObjectProperty<LocalDate>(transactionDate);
    this.volume = new SimpleDoubleProperty(volume);
    this.price = new SimpleDoubleProperty(price);
}

// getters
public String getBuySell(){
    return this.buySell.get();
}

// return Property Object
public StringProperty buySellProperty(){
    return this.buySell;
}

// setters
public void setBuySell(String buySell){
    this.buySell.set(buySell);
}

public LocalDate getTransactionDate(){
    return this.transactionDate.getValue();
}

public ObjectProperty<LocalDate> transactionDateProperty(){
    return this.transactionDate;
}

public void setTransactionDate(LocalDate transactionDate){
    this.transactionDate.set(transactionDate);
}


public double getVolume(){
    return this.volume.get();
}

public DoubleProperty volumeProperty(){
    return this.volume;
}

public void setVolume(double volume){
    this.volume.set(volume);
}


public double getPrice(){
    return this.price.get();
}   

public DoubleProperty priceProperty(){
    return this.price;
}

public void setPrice(double price){
    this.price.set(price);
}

public String getItemName(){
    return this.itemName.getValue();
}


public double getTransactionFee(){
        this.transactionFee = this.price.multiply(this.volume).multiply(0.15);
        return this.transactionFee.getValue();
}

public DoubleBinding transactionFeeProperty(){
    return this.transactionFee;
}



public String toString(){
    return "Buy: " + getBuySell() + ", Transasction date: " + getTransactionDate() + ", Volume: " + getVolume() + ", Price: " + getPrice() + ", Transaction fee: " + getTransactionFee() ;
}
}

Upvotes: 1

Views: 7122

Answers (1)

James_D
James_D

Reputation: 209684

Your listener should be responding to items being removed; if it's not, then there may be something wrong with the code you haven't shown.

For a ListChangeListener to respond to updates to properties belonging to elements of the list, you need to create your list specifying an extractor.

The extractor is a function that takes an element of the list, and returns an array of observable values. The list then observes all the values in that array, and if they change, fires update events to listeners of the list.

So, if you wanted your listener to be notified if any of the properties changed, you would do

ObservableList<Trade> observableListOfTrades =FXCollections.observableArrayList(trade ->
    new Observable[] {
        trade.transactionDateProperty(),
        trade.itemNameProperty(),
        trade.buySellProperty(),
        trade.volumeProperty(),
        trade.priceProperty().
        trade.transactionFeeProperty()
    });

Here is a complete example, using the usual "contact table" example:

import java.util.function.Function;

import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.geometry.HPos;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TableViewWithUpdateListenerExample extends Application {

    @Override
    public void start(Stage primaryStage) {

        // Create an observable list with an extractor. This will ensure
        // listeners on the list receive notifications if any of the
        // properties returned by the extractor belonging to a list element
        // are changed:

        ObservableList<Person> data = FXCollections.observableArrayList(person ->
            new Observable[] {
                    person.firstNameProperty(),
                    person.lastNameProperty(),
                    person.emailProperty()
            });

        data.addListener((Change<? extends Person> c) -> {
           while (c.next()) {
               if (c.wasAdded()) {
                   System.out.println("Added:");
                   c.getAddedSubList().forEach(System.out::println);
                   System.out.println();
               }
               if (c.wasRemoved()) {
                   System.out.println("Removed:");
                   c.getRemoved().forEach(System.out::println);
                   System.out.println();
               }
               if (c.wasUpdated()) {
                   System.out.println("Updated:");
                   data.subList(c.getFrom(), c.getTo()).forEach(System.out::println);
                   System.out.println();
               }
           }
        });

        data.addAll(
                new Person("Jacob", "Smith", "[email protected]"),
                new Person("Isabella", "Johnson", "[email protected]"),
                new Person("Ethan", "Williams", "[email protected]"),
                new Person("Emma", "Jones", "[email protected]"),
                new Person("Michael", "Brown", "[email protected]")
        );

        TableView<Person> tableView = new TableView<>();
        tableView.setEditable(true);
        tableView.setItems(data);

        tableView.getColumns().add(column("First Name", Person::firstNameProperty));
        tableView.getColumns().add(column("Last Name", Person::lastNameProperty));
        tableView.getColumns().add(column("Email", Person::emailProperty));

        TextField firstNameTF = new TextField();
        TextField lastNameTF = new TextField();
        TextField emailTF = new TextField();
        Button addButton = new Button("Add");
        addButton.setOnAction(e -> {
            Person person = new Person(firstNameTF.getText(), lastNameTF.getText(), emailTF.getText());
            firstNameTF.setText("");
            lastNameTF.setText("");
            emailTF.setText("");
            data.add(person);
        });

        GridPane editPane = new GridPane();
        editPane.addRow(0,  new Label("First Name:"), firstNameTF);
        editPane.addRow(1,  new Label("Last Name:"), lastNameTF);
        editPane.addRow(2,  new Label("Email:"), emailTF);
        editPane.add(addButton, 0, 3, 2, 1);
        ColumnConstraints leftCol = new ColumnConstraints();
        leftCol.setHalignment(HPos.RIGHT);
        leftCol.setHgrow(Priority.NEVER);
        editPane.setHgap(10);
        editPane.setVgap(5);
        editPane.getColumnConstraints().addAll(leftCol, new ColumnConstraints());

        Button deleteButton = new Button("Delete");
        deleteButton.setOnAction(e -> data.remove(tableView.getSelectionModel().getSelectedItem()));
        deleteButton.disableProperty().bind(Bindings.isEmpty(tableView.getSelectionModel().getSelectedItems()));

        VBox root = new VBox(10, tableView, editPane, deleteButton);
        root.setAlignment(Pos.CENTER);

        primaryStage.setScene(new Scene(root, 800, 600));
        primaryStage.show();
    }

    private TableColumn<Person, String> column(String title, Function<Person, ObservableValue<String>> property) {
        TableColumn<Person, String> col = new TableColumn<>(title);
        col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
        col.setCellFactory(TextFieldTableCell.forTableColumn());
        return col ;
    }

    public static class Person {
        private final StringProperty firstName = new SimpleStringProperty() ;
        private final StringProperty lastName = new SimpleStringProperty() ;
        private final StringProperty email = new SimpleStringProperty() ;

        public Person(String firstName, String lastName, String email) {
            setFirstName(firstName);
            setLastName(lastName);
            setEmail(email);
        }

        public final StringProperty firstNameProperty() {
            return this.firstName;
        }

        public final java.lang.String getFirstName() {
            return this.firstNameProperty().get();
        }

        public final void setFirstName(final java.lang.String firstName) {
            this.firstNameProperty().set(firstName);
        }

        public final StringProperty lastNameProperty() {
            return this.lastName;
        }

        public final java.lang.String getLastName() {
            return this.lastNameProperty().get();
        }

        public final void setLastName(final java.lang.String lastName) {
            this.lastNameProperty().set(lastName);
        }

        public final StringProperty emailProperty() {
            return this.email;
        }

        public final java.lang.String getEmail() {
            return this.emailProperty().get();
        }

        public final void setEmail(final java.lang.String email) {
            this.emailProperty().set(email);
        }

        @Override
        public String toString() {
            return getFirstName() + " " + getLastName() + " (" + getEmail() +")";
        }

    }

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

Upvotes: 10

Related Questions