【问题标题】:How to set CellValueFactory to functions of various properties in javafx?如何将 CellValueFactory 设置为 javafx 中各种属性的函数?
【发布时间】:2018-09-19 05:06:47
【问题描述】:

我正在使用 MVC 模式的 javafx 构建下载管理器,但遇到了一些麻烦。我想在 TableView 中列出下载,并在各个列中列出所有属性。

假设我有一个这样的模型。

class Download{
    private SimpleObjectProperty<Integer> part1;
    private SimpleObjectProperty<Integer> part2;

    public SimpleObjectProperty<Integer> getPart1Property(){
        return part1;
    }
    public SimpleObjectProperty<Integer> getPart2Property(){
        return part2;
    }
}

现在我用这样的表格视图设置了 javafx GUI。

TableView<Download> table=new TableView<Download>();
table.setItems(.. Observable list of Download objects ...);

如果我想在表格视图中为 part1 的值添加一列,我可以这样做。

 TableColumn<Download,Integer> part1 = new TableColumn<Download,Integer>("Part 1 value");
 part1.setCellValueFactory(
            (TableColumn.CellDataFeatures<Download, Integer> download) -> download.getValue().getPart1Property());

现在我需要在表格视图中有一列显示 part1 和 part2 的值的总和如何在 javafx 中实现这一点?

我已阅读文档here,但遗憾的是找不到任何方法可以实现这一点。任何帮助将不胜感激。

【问题讨论】:

  • 与您的问题无关:请遵循 fx 命名约定 - 在您的数据类中为 part1Property()not:getPart1Property())。也不要过度指定返回值的类型,使用 StringProperty (not SimpleStringProperty)
  • 谢谢!我会记住这一点。

标签: java javafx model-view-controller tableview


【解决方案1】:

您需要在 cellValueFactory 中返回所需值的总和。

TableColumn<Download, Integer> totalCol = new TableColumn<>("Total");
        totalCol.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getPart1() + param.getValue().getPart2()));

一个完整的工作演示如下:

import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;

public class CustomCellValueFactoryDemo extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        ObservableList<Download>  downloads = FXCollections.observableArrayList();
        downloads.add(new Download(23,56));
        downloads.add(new Download(56,43));
        downloads.add(new Download(97,3));
        downloads.add(new Download(67,23));

        TableView<Download> tableView = new TableView<>();
        TableColumn<Download, Integer> part1Col = new TableColumn<>("Part 1");
        part1Col.setCellValueFactory(param -> param.getValue().part1Property());

        TableColumn<Download, Integer> part2Col = new TableColumn<>("Part 2");
        part2Col.setCellValueFactory(param -> param.getValue().part2Property());

        TableColumn<Download, Integer> totalCol = new TableColumn<>("Total");
        totalCol.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getPart1() + param.getValue().getPart2()));


        tableView.getColumns().addAll(part1Col,part2Col, totalCol);
        tableView.setItems(downloads);

        Scene sc = new Scene(tableView);
        primaryStage.setScene(sc);
        primaryStage.show();
    }

    class Download{
        SimpleObjectProperty<Integer> part1 = new SimpleObjectProperty<>(0);
        SimpleObjectProperty<Integer> part2= new SimpleObjectProperty<>(0);
        public Download(int p1, int p2){
            part1.set(p1);
            part2.set(p2);

        }

        public int getPart1() {
            return part1.get();
        }

        public SimpleObjectProperty<Integer> part1Property() {
            return part1;
        }

        public void setPart1(int part1) {
            this.part1.set(part1);
        }

        public int getPart2() {
            return part2.get();
        }

        public SimpleObjectProperty<Integer> part2Property() {
            return part2;
        }

        public void setPart2(int part2) {
            this.part2.set(part2);
        }
    }
}

【讨论】:

  • 有问题。如果稍后在程序中更改 part1 的值,part1 列的值会正确更改,但 total 列的值不会更改。这是因为您在设置 cellValueFactory 时正在创建一个具有常量值的新 SimpleObjectProperty 对象。在我的程序中,part1 的值不断变化,因此上述解决方案不起作用将downloads.get(1).setPart1(300); 放在primaryStage.show() 行之后以复制我的情况。总计列应显示 343,但改为显示 99。
【解决方案2】:

没关系。事实证明我需要使用绑定来完成我正在寻找的东西。 以下是来自@Sai 的改进代码示例,它可以满足我的需要。相关文档为here

import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import javafx.beans.binding.Bindings;

public class test extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        ObservableList<Download>  downloads = FXCollections.observableArrayList();
        downloads.add(new Download(23,56));
        downloads.add(new Download(56,43));
        downloads.add(new Download(97,3));
        downloads.add(new Download(67,23));

        TableView<Download> tableView = new TableView<>();
        TableColumn<Download, Integer> part1Col = new TableColumn<>("Part 1");
        part1Col.setCellValueFactory(param -> param.getValue().part1Property());

        TableColumn<Download, Integer> part2Col = new TableColumn<>("Part 2");
        part2Col.setCellValueFactory(param -> param.getValue().part2Property());

        TableColumn<Download, Integer> totalCol = new TableColumn<>("Total");
        totalCol.setCellValueFactory(param -> {
            return Bindings.createObjectBinding(()-> { return (param.getValue().getPart1()+param.getValue().getPart2());} ,param.getValue().part1Property(),param.getValue().part2Property());
            });


        tableView.getColumns().addAll(part1Col,part2Col, totalCol);
        tableView.setItems(downloads);
        Scene sc = new Scene(tableView);
        primaryStage.setScene(sc);
        primaryStage.show();
        downloads.get(1).setPart1(300);
    }

    class Download{
        SimpleObjectProperty<Integer> part1 = new SimpleObjectProperty<>(0);
        SimpleObjectProperty<Integer> part2= new SimpleObjectProperty<>(0);
        public Download(int p1, int p2){
            part1.set(p1);
            part2.set(p2);

        }

        public int getPart1() {
            return part1.get();
        }

        public SimpleObjectProperty<Integer> part1Property() {
            return part1;
        }

        public void setPart1(int part1) {
            this.part1.set(part1);
        }

        public int getPart2() {
            return part2.get();
        }

        public SimpleObjectProperty<Integer> part2Property() {
            return part2;
        }

        public void setPart2(int part2) {
            this.part2.set(part2);
        }
    }
}

【讨论】:

    【解决方案3】:

    好的,考虑到您的要求,我认为首先您需要将属性定义为 IntegerProperty 而不是将 Integer 包装在 ObjectProperty 中(根据 javafx 标准)。这样,您可以直接使用 IntegerProperty 提供的数字绑定进行加法。当然,通过此更改,您需要将 TableColumn 数据类型定义为 Number。

    如果您非常热衷于仅将列的数据类型定义为 Integer,那么您可以在 cellValueFactory 中调用 asObject() 方法。就我个人而言,我不喜欢使用 asObject() 并强制转换为 IntegerBinding 来创建额外的对象层 :)

    IntegerProperty part1 = new SimpleIntegerProperty();
    
    TableColumn<Download, Integer> part1Col = new TableColumn<>("Part 1");
    part1Col.setCellValueFactory(param -> param.getValue().part1Property().asObject());
    
    TableColumn<Download, Integer> totalCol = new TableColumn<>("Total");
        totalCol.setCellValueFactory(param -> ((IntegerBinding)param.getValue().part1Property().add(param.getValue().part2Property())).asObject());
    

    下面是前一个demo的更新代码(更简洁一点),上面做了一些改动,第一行的Part1值会自动更新,总列值会自动更新。

    import javafx.animation.KeyFrame;
    import javafx.animation.Timeline;
    import javafx.application.Application;
    import javafx.beans.property.IntegerProperty;
    import javafx.beans.property.SimpleIntegerProperty;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.scene.Scene;
    import javafx.scene.control.TableColumn;
    import javafx.scene.control.TableView;
    import javafx.stage.Stage;
    import javafx.util.Duration;
    
    public class CustomCellValueFactoryDemo extends Application {
        @Override
        public void start(Stage primaryStage) throws Exception {
            ObservableList<Download> downloads = FXCollections.observableArrayList();
            downloads.add(new Download(23, 56));
            downloads.add(new Download(56, 43));
            downloads.add(new Download(97, 3));
            downloads.add(new Download(67, 23));
    
            TableView<Download> tableView = new TableView<>();
            TableColumn<Download, Number> part1Col = new TableColumn<>("Part 1");
            part1Col.setCellValueFactory(param -> param.getValue().part1Property());
    
            TableColumn<Download, Number> part2Col = new TableColumn<>("Part 2");
            part2Col.setCellValueFactory(param -> param.getValue().part2Property());
    
            TableColumn<Download, Number> totalCol = new TableColumn<>("Total");
            totalCol.setCellValueFactory(param -> param.getValue().part1Property().add(param.getValue().part2Property()));
    
    
            tableView.getColumns().addAll(part1Col, part2Col, totalCol);
            tableView.setItems(downloads);
    
            Scene sc = new Scene(tableView);
            primaryStage.setScene(sc);
            primaryStage.show();
    
            // Updating the value dynamically.
            Download firstRow = downloads.get(0);
            Timeline tl = new Timeline(new KeyFrame(Duration.millis(1000), e -> firstRow.setPart1(firstRow.getPart1() + 1)));
            tl.setCycleCount(50);
            tl.play();
        }
    
        class Download {
            IntegerProperty part1 = new SimpleIntegerProperty();
            IntegerProperty part2 = new SimpleIntegerProperty();
    
            public Download(int p1, int p2) {
                part1.set(p1);
                part2.set(p2);
    
            }
    
            public int getPart1() {
                return part1.get();
            }
    
            public IntegerProperty part1Property() {
                return part1;
            }
    
            public void setPart1(int part1) {
                this.part1.set(part1);
            }
    
            public int getPart2() {
                return part2.get();
            }
    
            public IntegerProperty part2Property() {
                return part2;
            }
    
            public void setPart2(int part2) {
                this.part2.set(part2);
            }
        }
    }
    

    【讨论】:

    • 请不要发布多个答案 - 而是编辑一个并添加选项(或决定哪个是您最喜欢的并只保留那个)顺便说一句,不知道您在 按照外汇标准
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-04
    • 2012-04-12
    • 1970-01-01
    • 1970-01-01
    • 2015-12-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多