JavaFX .如何在table的底部做一​​行求和(总行)?

JavaFX .How to make a line of summation(total row) in the bottom of the table?

如何在 table(TreeTableView - JavaFX 或 TableView) 的底部做一​​行求和? (对不起我的英语不好) 请写一个例子。

图片(总计)例如 http://i.stack.imgur.com/uRLTZ.gif

在每一栏下面我都放了一个标签来显示SUM.But这对我来说不好

一些FXML `

                                    <AnchorPane fx:id="anchorPaneWithTable" minHeight="0.0" minWidth="0.0" prefHeight="538.0" prefWidth="1275.0">
                                        <children>
                                            <TreeTableView fx:id="treeTableMainStat" prefHeight="386.0" prefWidth="1303.0" showRoot="false" AnchorPane.bottomAnchor="11.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0">
                                                <columns>
                                                    <TreeTableColumn fx:id="treeProgramNameCol" editable="false" prefWidth="230.0" resizable="false" text="AP Name" />
                                                    <TreeTableColumn fx:id="treeLastDateCol" editable="false" prefWidth="90.0" resizable="false" text="Last Date" />
                                                    <TreeTableColumn fx:id="treeLoginCol" editable="false" prefWidth="70.0" resizable="false" text="Login" />
                                                    <TreeTableColumn fx:id="treeAffiliateIDCol" editable="false" prefWidth="92.0" resizable="false" text="Affiliate ID" />
                                                    <TreeTableColumn fx:id="treeRawClicksCol" editable="false" resizable="false" text="Raw" />
                                                    <TreeTableColumn fx:id="treeUniqueClicksCol" editable="false" resizable="false" text="Uniq" />
                                                    <TreeTableColumn fx:id="treeSignupCounterCol" editable="false" prefWidth="67.0" resizable="false" text="SignupC" />
                                                    <TreeTableColumn fx:id="treeSignupMoneyCol" editable="false" minWidth="3.0" prefWidth="77.0" resizable="false" text="SignupM" />
                                                    <TreeTableColumn fx:id="treeRebillCounterCol" editable="false" minWidth="0.0" prefWidth="66.0" resizable="false" text="RebillC" />
                                                    <TreeTableColumn fx:id="treeRebillMoneyCol" editable="false" prefWidth="78.0" resizable="false" text="RebillM" />
                                                    <TreeTableColumn fx:id="treeRefundChargebackCounterCol" editable="false" prefWidth="105.0" resizable="false" text="R/Ch Counter" />
                                                    <TreeTableColumn fx:id="treeRefundChargebackMoneyCol" editable="false" prefWidth="104.0" resizable="false" text="R/Ch Money" />
                                                    <TreeTableColumn fx:id="treeTotalMoneyCol" editable="false" minWidth="0.0" prefWidth="110.0" resizable="false" text="Total Money" />
                                                </columns>
                                                <contextMenu>
                                                    <ContextMenu>
                                                        <items>
                                                            <MenuItem mnemonicParsing="false" onAction="#handleAddNewAffiliateProgram" text="Add new AP" />
                                                            <MenuItem mnemonicParsing="false" text="Unspecified Action" />
                                                            <MenuItem mnemonicParsing="false" text="Unspecified Action" />
                                                            <MenuItem mnemonicParsing="false" onAction="#handleDeleteAffiliateProgram" text="Delete AP" />
                                                            <SeparatorMenuItem mnemonicParsing="false" />
                                                            <MenuItem mnemonicParsing="false" onAction="#handleEditAP" text="Properties" />
                                                        </items>
                                                    </ContextMenu>
                                                </contextMenu>
                                            </TreeTableView>
                                            <ProgressIndicator fx:id="refreshingIndicator" layoutX="478.0" layoutY="124.0" prefHeight="250.0" prefWidth="374.0" progress="-1.0" AnchorPane.leftAnchor="478.0" AnchorPane.topAnchor="124.0" />
                                        </children>
                                    </AnchorPane>
                                </content>
                            </Tab>
                            <Tab closable="false" text="Empty Tab">
                                <content>
                                    <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" visible="false" />
                                </content>
                            </Tab>
                        </tabs>
                    </TabPane>
                    <HBox prefHeight="35.0">
                        <children>
                            <Label fx:id="apNameLab" prefHeight="30.0" prefWidth="229.0" text="Label" />
                            <Separator orientation="VERTICAL" prefHeight="200.0" />
                            <Label fx:id="lastDateLab" prefHeight="30.0" prefWidth="85.0" text="LastDate" />
                            <Separator orientation="VERTICAL" prefHeight="200.0" />
                            <Label fx:id="loginLab" prefHeight="30.0" prefWidth="64.0" text="Login" />
                            <Separator orientation="VERTICAL" prefHeight="200.0" />
                            <Label fx:id="affilIdLab" prefHeight="30.0" prefWidth="86.0" text="AffID" />
                            <Separator orientation="VERTICAL" prefHeight="200.0" />
                            <Label fx:id="rawLab" prefHeight="30.0" prefWidth="74.0" text="rawLab" />
                            <Separator orientation="VERTICAL" prefHeight="200.0" />
                            <Label fx:id="uniqLab" prefHeight="30.0" prefWidth="74.0" text="uniqLab" />
                            <Separator orientation="VERTICAL" prefHeight="200.0" />
                            <Label fx:id="signCLab" prefHeight="30.0" prefWidth="61.0" text="SignCLab" />
                            <Separator orientation="VERTICAL" prefHeight="200.0" />
                            <Label fx:id="signMLab" prefHeight="30.0" prefWidth="71.0" text="SignMLab" />
                            <Separator orientation="VERTICAL" prefHeight="200.0" />
                            <Label fx:id="rebillCLab" prefHeight="30.0" prefWidth="61.0" text="RebillCLab" />
                            <Separator orientation="VERTICAL" prefHeight="200.0" />
                            <Label fx:id="rebillMLab" prefHeight="30.0" prefWidth="72.0" text="RebillMLab" />
                            <Separator orientation="VERTICAL" prefHeight="200.0" />
                            <Label fx:id="rChCountLab" prefHeight="30.0" prefWidth="99.0" text="RChCounLab" />
                            <Separator orientation="VERTICAL" prefHeight="200.0" />
                            <Label fx:id="rChMonLab" prefHeight="30.0" prefWidth="98.0" text="RChMonLab" />
                            <Separator orientation="VERTICAL" prefHeight="200.0" />
                            <Label fx:id="totalLab" prefHeight="30.0" prefWidth="110.0" text="TotalLab" />
                        </children>
                        <padding>
                            <Insets left="10.0" />
                        </padding>
                    </HBox>
                </children>
            </VBox>`

将标签绑定到列

//TreeView_Begin
@FXML
private TreeTableView<APDataFromDB> treeTableMainStat;
@FXML
private TreeTableColumn<APDataFromDB, Integer> treeProgramNameCol;
@FXML
private TreeTableColumn<APDataFromDB, Integer> treeLastDateCol;
@FXML
private TreeTableColumn<APDataFromDB, Integer> treeLoginCol;
@FXML
private TreeTableColumn<APDataFromDB, Integer> treeAffiliateIDCol;
@FXML
private TreeTableColumn<APDataFromDB, Integer> treeRawClicksCol;
@FXML
private TreeTableColumn<APDataFromDB, Integer> treeUniqueClicksCol;
@FXML
private TreeTableColumn<APDataFromDB, Integer> treeSignupCounterCol;
@FXML
private TreeTableColumn<APDataFromDB, Double> treeSignupMoneyCol;
@FXML
private TreeTableColumn<APDataFromDB, Integer> treeRebillCounterCol;
@FXML
private TreeTableColumn<APDataFromDB, Double> treeRebillMoneyCol;
@FXML
private TreeTableColumn<APDataFromDB, Integer> treeRefundChargebackCounterCol;
@FXML
private TreeTableColumn<APDataFromDB, Double> treeRefundChargebackMoneyCol;
@FXML
private TreeTableColumn<APDataFromDB, Double> treeTotalMoneyCol;
//TreeView_End


@FXML
Label apNameLab = new Label();
@FXML
Label lastDateLab = new Label();
@FXML
Label loginLab = new Label();
@FXML
Label affilIdLab = new Label();
@FXML
Label rawLab = new Label();
@FXML
Label uniqLab = new Label();
@FXML
Label signCLab = new Label();
@FXML
Label signMLab = new Label();
@FXML
Label rebillCLab = new Label();
@FXML
Label rebillMLab = new Label();
@FXML
Label rChCountLab = new Label();
@FXML
Label rChMonLab = new Label();
@FXML
Label totalLab = new Label();

        @FXML
    private void initialize() throws SQLException {

        final String pattern = "yyyy-MM-dd";

        datePickerStart.setValue(LocalDate.now());
        datePickerEnd.setValue(LocalDate.now());
        allDownloadsProgressBar.setProgress(0);
        refreshingIndicator.setVisible(false);

        StringConverter stringConverter = new StringConverter<LocalDate>() {
            DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(pattern);

            @Override
            public String toString(LocalDate date) {
                if (date != null) {
                    return dateFormatter.format(date);
                } else {
                    return "";
                }
            }

            @Override
            public LocalDate fromString(String string) {
                if (string != null && !string.isEmpty()) {
                    return LocalDate.parse(string, dateFormatter);
                } else {
                    return null;
                }
            }
        };
        datePickerStart.setConverter(stringConverter);
        datePickerStart.setPromptText(pattern.toLowerCase());
        datePickerEnd.setConverter(stringConverter);
        datePickerEnd.setPromptText(pattern.toLowerCase());

        // устанавливаем тип и значение которое должно хранится в колонке
        //TreeView_begin
        treeProgramNameCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("apName"));
        treeLastDateCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("lastDate"));
        treeLoginCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("login"));
        treeAffiliateIDCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("affiliateID"));
        treeRawClicksCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("raw"));
        treeUniqueClicksCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("uniq"));
        treeSignupCounterCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("signupCounter"));
        treeSignupMoneyCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("signup"));
        treeRebillCounterCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("rebillCounter"));
        treeRebillMoneyCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("rebillMoney"));
        treeRefundChargebackCounterCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("refundChargebackCounter"));
        treeRefundChargebackMoneyCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("refundChargebackMoney"));
        treeTotalMoneyCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("totalMoney"));

        // заполняем таблицу данными

        //        Platform.runLater(new Runnable() {
//            @Override
//            public void run() {
//
//            }
//        });
        new Thread(new Runnable() {
            @Override
            public void run() {
                buildTreeOfStatsFromDB();
                //Binding
                apNameLab.prefWidthProperty().bind(treeProgramNameCol.widthProperty().multiply(1));
                lastDateLab.prefWidthProperty().bind(treeLastDateCol.widthProperty().multiply(1));
                loginLab.prefWidthProperty().bind(treeLoginCol.widthProperty().multiply(1));
                affilIdLab.prefWidthProperty().bind(treeAffiliateIDCol.widthProperty().multiply(1));
                rawLab.prefWidthProperty().bind(treeRawClicksCol.widthProperty().multiply(1));
                uniqLab.prefWidthProperty().bind(treeUniqueClicksCol.widthProperty().multiply(1));
                signCLab.prefWidthProperty().bind(treeSignupCounterCol.widthProperty().multiply(1));
                signMLab.prefWidthProperty().bind(treeSignupMoneyCol.widthProperty().multiply(1));
                rebillCLab.prefWidthProperty().bind(treeRebillCounterCol.widthProperty().multiply(1));
                rebillMLab.prefWidthProperty().bind(treeRebillMoneyCol.widthProperty().multiply(1));
                rChCountLab.prefWidthProperty().bind(treeRefundChargebackCounterCol.widthProperty().multiply(1));
                rChMonLab.prefWidthProperty().bind(treeRefundChargebackMoneyCol.widthProperty().multiply(1));
                totalLab.prefWidthProperty().bind(treeTotalMoneyCol.widthProperty().multiply(1));

                getSumLabel();

            }
        }).start();

//        buildTreeOfStatsFromDB();


        // Enable arrow keys for delete AP
        treeTableMainStat.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent keyEvent) {
                if (keyEvent.getCode() == KeyCode.Y && keyEvent.isControlDown()) {
                    handleDeleteAffiliateProgram();
                    keyEvent.consume();
                }
            }
        });
    }

获取所有 SUM

</p> <pre><code>private void getSumLabel() { long rawHitsSum = 0; long uniqHitsSum = 0; long signCountSum = 0; double signMoneySum = 0; long rebillCountSum = 0; double rebillMoneySum = 0; long rChCountSum = 0; double rChMoneySum = 0; double totalMoneySum = 0; ObservableList<TreeItem<APDataFromDB>> allData = rootItem.getChildren(); for (TreeItem<APDataFromDB> apDataFromDBTreeItem : allData) { rawHitsSum += apDataFromDBTreeItem.getValue().getRaw(); uniqHitsSum += apDataFromDBTreeItem.getValue().getUniq(); signCountSum += apDataFromDBTreeItem.getValue().getSignupCounter(); signMoneySum += apDataFromDBTreeItem.getValue().getSignup(); rebillCountSum += apDataFromDBTreeItem.getValue().getRebillCounter(); rebillMoneySum += apDataFromDBTreeItem.getValue().getRebillMoney(); rChCountSum += apDataFromDBTreeItem.getValue().getRefundChargebackCounter(); rChMoneySum += apDataFromDBTreeItem.getValue().getRefundChargebackMoney(); totalMoneySum += apDataFromDBTreeItem.getValue().getTotalMoney(); } final long finalRawHitsSum = rawHitsSum; final long finalUniqHitsSum = uniqHitsSum; final long finalSignCountSum = signCountSum; final long finalSignCountSum1 = signCountSum; final long finalRebillCountSum = rebillCountSum; final double finalRebillMoneySum = rebillMoneySum; final long finalRChCountSum = rChCountSum; final double finalRChMoneySum = rChMoneySum; final double finalTotalMoneySum = totalMoneySum; Platform.runLater(new Runnable() { @Override public void run() { rawLab.setText(String.valueOf(finalRawHitsSum)); uniqLab.setText(String.valueOf(finalUniqHitsSum)); signCLab.setText(String.valueOf(finalSignCountSum1)); signMLab.setText(String.valueOf(finalSignCountSum)); rebillCLab.setText(String.valueOf(finalRebillCountSum)); rebillMLab.setText(String.valueOf(finalRebillMoneySum)); rChCountLab.setText(String.valueOf(finalRChCountSum)); rChMonLab.setText(String.valueOf(finalRChMoneySum)); totalLab.setText(String.valueOf(finalTotalMoneySum)); } }); System.out.println(rawHitsSum); }

编辑:不是问题的答案。我误解了问题。


我没有示例,但我可以向您解释一种方法。

  • 使用 "TableView"
  • 每一列应该是 "TableColumn"。这个 "TableColumn" 应该与 "ObsverbableList" 相关:

    YourColumn.setCellValueFactory(
            cellData ->CellData.getValue().yourObservableList.yourFloatProperty());
    
  • 列表中的数字应为 "FloatProperty"。

  • 为除最后一个以外的所有设置监听器。 (你可以循环执行此操作)
    • 每个听众都应该调用您的求和函数。此函数应计算列表的最后一个元素 = 列表中其他元素的总和
    • "TableView" 会自动更新。

在 JavaFX 中就像在 Swing 中一样:最快的方法是创建第二个 table。

你可以看看summary table sample code on this gist

截图:

我会通过实现一个 TransformationList 来做到这一点,它在实际数据列表的末尾添加一个额外的元素。

在我的 example 中,我有一个 TableView 显示 LineItems(我的模型 class),所以我的 TransformationList 实现看起来像:

public class LineItemListWithTotal extends TransformationList<LineItem, LineItem> {

    private final TotalLine totalLine ;

    protected LineItemListWithTotal(
            ObservableList<? extends LineItem> source) {
        super(source);
        totalLine = new TotalLine(source);
    }

    @Override
    protected void sourceChanged(Change<? extends LineItem> c) {

        // no need to modify change: 
        // indexes generated by the source list will match indexes in this list

        fireChange(c);
    }

    @Override
    public int getSourceIndex(int index) {
        if (index < getSource().size()) {
            return index ;
        }
        return -1 ;
    }

    @Override
    public LineItem get(int index) {
        if (index < getSource().size()) {
            return getSource().get(index);
        } else if (index == getSource().size()) {
            return totalLine ;
        } else throw new ArrayIndexOutOfBoundsException(index);
    }

    @Override
    public int size() {
        return getSource().size() + 1 ;
    }

}

您可能会发现您需要模型 class 的特殊子class 以便轻松完成这项工作(在本例中我子classed LineItem TotalLine)。

使用提取器创建您的实际数据列表,该提取器检索您在计算 "total" 行时需要观察的属性(即该行所依赖的属性):

ObservableList<LineItem> items = FXCollections.observableArrayList(item -> 
    new Observable[] {item.totalProperty()});

然后用它来创建 TransformationList 并将 TransformationList 传递给控件:

table.setItems(new LineItemListWithTotal(items));

我在特殊子class中管理总行的观察者实现总计:

public class TotalLine extends LineItem {

    private final ReadOnlyObjectWrapper<Double> total = new ReadOnlyObjectWrapper<>();

    public TotalLine(ObservableList<? extends LineItem> items) {
        super("Total", null, null);

        total.bind(Bindings.createObjectBinding(() -> 
                items.stream().collect(Collectors.summingDouble(LineItem::getTotal)), items));
    }

    @Override
    public ReadOnlyObjectProperty<Double> totalProperty() {
        return total ;
    }
}

这样,当向原始数据列表添加或从原始数据列表中删除任何内容时,或者如果提取器中任何元素的任何属性发生变化,将重新计算总计行的总数。

完整的例子是here