JavaFX Tableview - column value depends on other columns

I have a TableView in JavaFX. It has a subTotal field, which depends on the value of the quantity and price fields. I added a new column for subTotal .

I have text fields to add a new row to the table. But the add button also wants to have a textfield for subTotal , although this is not necessary for the subtotal column.

What I have tried so far:

 TableColumn columnCodeProduct = new TableColumn("Product Code"); columnCodeProduct.setMinWidth(100); columnCodeProduct.setCellValueFactory(new PropertyValueFactory<Data , Integer>("productname ")); TableColumn columnProductName = new TableColumn("Product Name"); columnProductName.setMinWidth(140); columnProductName.setCellValueFactory(new PropertyValueFactory<Data , String>("codeproduct")); TableColumn columnPrice = new TableColumn("Price"); columnPrice.setMinWidth(100); columnPrice.setCellValueFactory(new PropertyValueFactory<Data , Integer>("price")); TableColumn columQuantity = new TableColumn("Quantity"); columQuantity.setMinWidth(100); columQuantity.setCellValueFactory(new PropertyValueFactory<Data , Integer>("quantity")); TableColumn columnTotal = new TableColumn("Sub Total"); columnTotal.setMinWidth(100); columQuantity.setCellValueFactory(new PropertyValueFactory<Data , Integer>("sub")); tableData.getColumns().addAll(columnCodeProduct , columnProductName , columnPrice , columQuantity ); tableData.setItems(data); addButton = new Button("Add Item"); addButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { if(addproCodeTextfield.getText().isEmpty() || addproNameTextfield.getText().isEmpty() || addPriceTextfield.getText().isEmpty() || quantityTextField.getText().isEmpty()) { System.out.println("Please Add information to all the fields"); } else { data.add(new Data ( addproCodeTextfield.getText(), addproNameTextfield.getText(), addPriceTextfield.getText(), quantityTextField.getText())); methodTotal(); } } }); 

Data class

 public class Data { private final SimpleStringProperty codeproduct; private final SimpleStringProperty productname; private final SimpleStringProperty price ; private final SimpleStringProperty quantity; public Data (String code , String proname , String presyo , String quant ) { this.codeproduct = new SimpleStringProperty(code); this.productname = new SimpleStringProperty(proname); this.price = new SimpleStringProperty(presyo); this.quantity = new SimpleStringProperty(quant); } public String getcodeProduct() { return codeproduct.get(); } public String getproductName() { return productname.get(); } public String getPrice() { return price.get(); } public String getQuantity() { return quantity.get(); } } 
+7
javafx
source share
2 answers

You can take advantage of the power of JavaFX up to bind .

A few points to follow when implementing the scenario, as described above:

  • POJO fields (in your case Data ) must have the correct types. For example, price and quantity should be SimpleIntegerProperty instead of SimpleStringProperty . This will help us use Bindings . Field
  • SubTotal depends on the price and quantity. The best way to achieve this is to bind subTotalProperty to a multiply Binding of price and quantity .

I created a (not so) simple example of a basic editable table view to show this approach. It has additional features, such as editable cells, that you (or others looking for the same problem) may need;)

 import javafx.application.Application; import javafx.beans.binding.Bindings; import javafx.beans.binding.NumberBinding; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.control.TableColumn.CellEditEvent; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.control.cell.TextFieldTableCell; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.stage.Stage; import javafx.util.converter.NumberStringConverter; public class TableViewSample extends Application { private TableView<Product> table = new TableView<Product>(); private final ObservableList<Product> data = FXCollections.observableArrayList( new Product("Notebook", 10, 12), new Product("Eraser", 20, 12), new Product("Pencil", 30, 12), new Product("Pen", 40, 12), new Product("Glue", 50, 12)); final HBox hb = new HBox(); public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { Scene scene = new Scene(new Group()); stage.setTitle("Book Store Sample"); stage.setWidth(650); stage.setHeight(550); final Label label = new Label("Book Store"); label.setFont(new Font("Arial", 20)); table.setEditable(true); TableColumn name = new TableColumn("Name"); name.setMinWidth(100); name.setCellValueFactory( new PropertyValueFactory<Product, String>("name")); name.setCellFactory(TextFieldTableCell.forTableColumn()); name.setOnEditCommit( new EventHandler<CellEditEvent<Product, String>>() { @Override public void handle(CellEditEvent<Product, String> t) { ((Product) t.getTableView().getItems().get( t.getTablePosition().getRow()) ).setName(t.getNewValue()); } } ); TableColumn priceCol = new TableColumn("Price"); priceCol.setMinWidth(100); priceCol.setCellValueFactory( new PropertyValueFactory<Product, String>("price")); priceCol.setCellFactory(TextFieldTableCell.<Product, Number>forTableColumn(new NumberStringConverter())); priceCol.setOnEditCommit( new EventHandler<CellEditEvent<Product, Number>>() { @Override public void handle(CellEditEvent<Product, Number> t) { ((Product) t.getTableView().getItems().get( t.getTablePosition().getRow()) ).setPrice(t.getNewValue().intValue()); } } ); TableColumn quantityCol = new TableColumn("Quantity"); quantityCol.setMinWidth(200); quantityCol.setCellValueFactory( new PropertyValueFactory<Product, Number>("quantity")); quantityCol.setCellFactory(TextFieldTableCell.<Product, Number>forTableColumn(new NumberStringConverter())); quantityCol.setOnEditCommit( new EventHandler<CellEditEvent<Product, Number>>() { @Override public void handle(CellEditEvent<Product, Number> t) { ((Product) t.getTableView().getItems().get( t.getTablePosition().getRow()) ).setQuantity(t.getNewValue().intValue()); } } ); TableColumn subTotalCol = new TableColumn("Sub Total"); subTotalCol.setMinWidth(200); subTotalCol.setCellValueFactory( new PropertyValueFactory<Product, String>("subTotal")); table.setItems(data); table.getColumns().addAll(name, priceCol, quantityCol, subTotalCol); final TextField addName = new TextField(); addName.setPromptText("Name"); addName.setMaxWidth(name.getPrefWidth()); final TextField addPrice = new TextField(); addPrice.setMaxWidth(priceCol.getPrefWidth()); addPrice.setPromptText("Price"); final TextField addQuantity = new TextField(); addQuantity.setMaxWidth(quantityCol.getPrefWidth()); addQuantity.setPromptText("Quantity"); final Button addButton = new Button("Add"); addButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent e) { data.add(new Product( name.getText(), Integer.parseInt(addPrice.getText()), Integer.parseInt(addQuantity.getText()))); addName.clear(); addPrice.clear(); addQuantity.clear(); } }); hb.getChildren().addAll(addName, addPrice, addQuantity, addButton); hb.setSpacing(3); final VBox vbox = new VBox(); vbox.setSpacing(5); vbox.setPadding(new Insets(10, 0, 0, 10)); vbox.getChildren().addAll(label, table, hb); ((Group) scene.getRoot()).getChildren().addAll(vbox); stage.setScene(scene); stage.show(); } public static class Product { private final SimpleStringProperty name; private final SimpleIntegerProperty price; private final SimpleIntegerProperty quantity; private final SimpleIntegerProperty subTotal; private Product(String name, int price, int quantity) { this.name = new SimpleStringProperty(name); this.price = new SimpleIntegerProperty(price); this.quantity = new SimpleIntegerProperty(quantity); this.subTotal = new SimpleIntegerProperty(); NumberBinding multiplication = Bindings.multiply(this.priceProperty(), this.quantityProperty()); this.subTotalProperty().bind(multiplication); } public String getName() { return name.get(); } public SimpleStringProperty nameProperty() { return name; } public void setName(String name) { this.name.set(name); } public int getPrice() { return price.get(); } public SimpleIntegerProperty priceProperty() { return price; } public void setPrice(int price) { this.price.set(price); } public int getQuantity() { return quantity.get(); } public SimpleIntegerProperty quantityProperty() { return quantity; } public void setQuantity(int quantity) { this.quantity.set(quantity); } public int getSubTotal() { return subTotal.get(); } public SimpleIntegerProperty subTotalProperty() { return subTotal; } public void setSubTotal(int subTotal) { this.subTotal.set(subTotal); } } } 

Screenshot

enter image description here

Note I defined setCellFactory and setOnCommit for each column. This is because the columns of the name, price and quantity are editable . You can delete them if you are not looking for an editable property.

+5
source share

I would restructure your model class as @ItachiUchiha suggests. If you think that you need to save the data stored in String views, you can simply create a binding for the subtotal column:

 TableColumn<Data, Number> subtotalColumn = new TableColumn<>("Sub Total"); subTotalColumn.setCellValueFactory(cellData -> { Data data = cellData.getValue(); return Bindings.createDoubleBinding( () -> { try { double price = Double.parseDouble(data.getPrice()); int quantity = Integer.parseInt(data.getQuantity()); return price * quantity ; } catch (NumberFormatException nfe) { return 0 ; } }, data.priceProperty(), data.quantityProperty() ); }); 
+3
source share

All Articles