【问题标题】:JavaFX Tableview - column value dependent on other columnsJavaFX Tableview - 列值依赖于其他列
【发布时间】:2015-06-17 19:30:25
【问题描述】:

我在 JavaFX 中有一个 TableView。它有一个字段subTotal,它取决于字段quantityprice 的值。我为subTotal 添加了一个新列。

我有文本字段用于向表中添加新行。但是,添加按钮希望有另一个 textfield 用于 subTotal,尽管对于小计列并不需要。

到目前为止我已经尝试过什么:

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();
        }
    }
});

数据类

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();
    }

}

【问题讨论】:

    标签: javafx


    【解决方案1】:

    您可以从 JavaFX 的强大功能中受益于bind 的价值。

    在实现上述场景时需要注意的几点:

    • POJO 类(在您的情况下为 Data)字段必须具有正确的类型。例如价格和数量必须是SimpleIntegerProperty 而不是SimpleStringProperty。这将有助于我们使用Bindings
    • SubTotal 字段取决于价格和数量的值。实现此目的的最佳方法是绑定 subTotalProperty to a multiply Binding of price and quantity

    我创建了一个(不是那么)简单的基本示例可编辑的tableview 来展示该方法。它具有您(或其他寻求相同问题的人)可能需要的附加功能,例如可编辑单元格;)

    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);
            }
        }
    }
    

    截图

    注意 - 我已经为每一列定义了setCellFactorysetOnCommit。这是因为名称、价格和数量列是editable。如果您不寻求可编辑的属性,欢迎您删除它们。

    【讨论】:

    • 谢谢 :) 顺便说一下如何添加所有小计?
    • 这实际上可以是一个单独的问题:)
    【解决方案2】:

    我会按照@ItachiUchiha 的建议重构您的模型类。如果您觉得需要使用String 表示形式存储数据,您可以为subtotal 列创建一个绑定:

    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()
        );
    });
    

    【讨论】:

    • 谢谢,很简单! :) 我用它来更新一个字符串列,其值是从我的模型中的十六进制字符串计算出来的。供每个人考虑:我不想为无效数据显示有效的 0。所以我使用了一个字符串列。如果解析失败,它可以显示一个空字符串或“ERR”。 Afaik,数字列是不可能的。
    • 当我尝试在createIntegerBinding 中添加两个整数时出现“javafx.beans.binding.IntegerBinding 无法转换为 javafx.beans.value.ObservableValue”错误。
    猜你喜欢
    • 2013-09-18
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    • 2023-01-09
    • 2014-08-19
    • 1970-01-01
    • 1970-01-01
    • 2021-07-06
    相关资源
    最近更新 更多