JavaFX 嵌套 TableView 间距

问题描述

所以我正在尝试使用 JavaFX 创建一个嵌套表,但遇到了间距问题。我使用标准方法来创建主表,没有什么特别的。嵌套表虽然:我正在遵循我在此链接中找到的指南 https://edencoding.com/tableview-customization-cellfactory/ 一切都按照它应该的方式工作,但在我插入嵌套表时添加到行中的空间除外。我希望主表的行与整个嵌套表的高度相同,但它比这大得多,我似乎无法调整它的大小。

我尝试了以下方法

  1. 在 cell.itemProperty 侦听器中,我设置了单元格 maxHeight。
  2. 我设置了嵌套 TableView 的最大高度。
  3. 底部的第二个链接中,您可以看到我使用 ChoiceBox 而不是 TableView 时的图片。这确实使行具有正确的大小(它适合选择框)。
  4. 删除了 ConstrainedResize 政策
  5. 在从 cell.itemProperty 侦听器返回单元格之前,将单元格的最大高度设置为固定值。
  6. 就在从 cell.itemProperty 侦听器返回单元格之前调用 cell.resize(n,n)。

这是创建表格的代码。这个方法在控制器类中。 BlendedWordRecall 类是一个非常简单的 POJO,BlendClass 是一个接口,但同样,非常基本的 getter 和 setter 没有什么特别的。

private TableView getBlendingTableView(List<BlendedWordRecall> recalls) {
        ObservableList<BlendedWordRecall> data = FXCollections.observableArrayList();
        data.addAll(recalls);
        
        TableView<BlendedWordRecall> table = new TableView<BlendedWordRecall>();
        table.setMinWidth(1250);
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        TableColumn<BlendedWordRecall,String> wordCol = new TableColumn<BlendedWordRecall,String>("Word");
        wordCol.setMinWidth(150);
        wordCol.setCellValueFactory(cellData -> {
            return new SimpleStringProperty(cellData.getValue().getWord());
        });
        
        TableColumn<BlendedWordRecall,String> recallDateCol = new TableColumn<BlendedWordRecall,String>("Recall Date");
        recallDateCol.setMinWidth(230);
        recallDateCol.setCellValueFactory(cellData -> {
            LocalDateTime ldt = LocalDateTime.ofInstant(cellData.getValue().getDateOfRecall().toInstant(),ZoneId.systemDefault());
            return new SimpleStringProperty(ldt.format(dateTimeFormat));
        });
        
        TableColumn<BlendedWordRecall,String> correctCol = new TableColumn<BlendedWordRecall,String>("Correct?");
        correctCol.setMinWidth(230);
        correctCol.setCellValueFactory(cellData -> {
            String booleanString = cellData.getValue().isWasCorrect() == true ? "Correct" : "Incorrect";
            return new SimpleStringProperty(booleanString);
        });
        correctCol.setCellFactory(column -> {
            return new TableCell<BlendedWordRecall,String>() {
                @Override
                protected void updateItem(String item,boolean empty) {
                    super.updateItem(item,empty);
        
                    if (item == null || empty) {
                        setText(null);
                        setStyle("");
                    } else {
                        if (item.equals("Correct")) {
                            setText("Correct");
                            setStyle("  -fx-font-size: 14px;\r\n" + 
                                            "   -fx-font-family: \"Comic Sans MS\";\r\n" + 
                                            "   -fx-font-weight: bolder;\r\n" + 
                                            "    -fx-text-fill: #37ff00;");
                        } else {
                            setText("Incorrect");
                            setStyle("  -fx-font-size: 14px;\r\n" + 
                                            "   -fx-font-family: \"Comic Sans MS\";\r\n" + 
                                            "   -fx-font-weight: bolder;\r\n" + 
                                            "    -fx-text-fill: #E60000;");
                        }
                    }
                }
            };
        });
        
        TableColumn<BlendedWordRecall,String> sightCol = new TableColumn<BlendedWordRecall,String>("Sight Read?");
        sightCol.setMinWidth(150);
        sightCol.setCellValueFactory(cellData -> {
            String sightString = cellData.getValue().isWasSightRead() == true ? "True" : "False";
            return new SimpleStringProperty(sightString);
        });
        
        TableColumn<BlendedWordRecall,String> recallTimeCol = new TableColumn<BlendedWordRecall,String>("Recall Time");
        recallTimeCol.setMinWidth(150);
        recallTimeCol.setCellValueFactory(cellData -> {
            String timeString = String.format("%.2f",cellData.getValue().getRecallDuration())+" Sec";
            return new SimpleStringProperty(timeString);
        });

        TableColumn<BlendedWordRecall,String> blendsCol = new TableColumn<BlendedWordRecall,String>("Blends");
        blendsCol.setMinWidth(300);
        blendsCol.setCellValueFactory(cellData -> {
            return new SimpleStringProperty("yes");// Set just so the cell.itemProperty will fire all the time
        });
         blendsCol.setCellFactory(col -> {
            TableCell<BlendedWordRecall,String> cell = new TableCell<>();
            
            cell.itemproperty().addListener((obs,old,newVal) -> {
                if (newVal != null) {
                    //////////////////////////////// Create nested table //////////////////////////////////////
                    ObservableList<BlendClass> blendList = FXCollections.observableArrayList();
                    SingleLetterBlendRecall slbOne = new SingleLetterBlendRecall("a / A","A","b / B","B");
                    SingleLetterBlendRecall slbTwo = new SingleLetterBlendRecall("b / B","B","c / C","C");
                    blendList.addAll(slbOne,slbTwo);
                    TableView<BlendClass> blendTable = new TableView<BlendClass>();
                    blendTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
                    TableColumn<BlendClass,String> firstCol = new TableColumn<BlendClass,String>("First Sound");
                    firstCol.setMinWidth(150);
                    firstCol.setCellValueFactory(cellData -> {
                        return new SimpleStringProperty(cellData.getValue().getFirstSoundName());
                    });
                    
                    TableColumn<BlendClass,String> secondCol = new TableColumn<BlendClass,String>("Second Sound");
                    secondCol.setMinWidth(150);
                    secondCol.setCellValueFactory(cellData -> {
                        return new SimpleStringProperty(cellData.getValue().getSecondSoundName());
                    });
                    blendTable.getColumns().addAll(firstCol,secondCol);
                    blendTable.setItems(blendList);
                    blendTable.setMaxHeight(40 + (30*blendList.size()));
                    ///////////////////////////////////////////////////////////////////////////////////
                    cell.graphicproperty().bind(Bindings.when(cell.emptyproperty()).then((Node) null).otherwise(blendTable));
            }
        });
        return cell;
    });
        table.getColumns().addAll(wordCol,recallDateCol,correctCol,sightCol,recallTimeCol,blendsCol);
        table.setItems(data);
        
    return table;
}   

Table made with current code

Table made using a choice box instead of a nested table

Table made without resizing the nested table

基于第一张图片中的表格与第三张图片中的表格的行大小相同的事实,我很确定这是一个时间问题。就像在我调整表格或其他东西之前布局正在发生一样。任何关于从这里去哪里的想法都会很棒,谢谢大家。

解决方法

我终于明白了。希望它会帮助遇到同样问题的其他人。在构建嵌套表时,我最初只是设置最大高度,这使嵌套表具有正确的大小,但在我设置其最大高度之前,包含它的单元格是嵌套表的原始大小。显然,如果您设置嵌套表的最大、最小和首选高度,那么它的行为是正确的,因为包含嵌套表的单元格将根据嵌套表的大小调整大小。这是一张图片供参考。

Correct output

这是更新后的代码,只有两行更改:

private TableView getBlendingTableView(List<BlendedWordRecall> recalls) {
        ObservableList<BlendedWordRecall> data = FXCollections.observableArrayList();
        data.addAll(recalls);
        
        TableView<BlendedWordRecall> table = new TableView<BlendedWordRecall>();
        table.setMinWidth(1250);
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        TableColumn<BlendedWordRecall,String> wordCol = new TableColumn<BlendedWordRecall,String>("Word");
        wordCol.setMinWidth(150);
        wordCol.setCellValueFactory(cellData -> {
            return new SimpleStringProperty(cellData.getValue().getWord());
        });
        
        TableColumn<BlendedWordRecall,String> recallDateCol = new TableColumn<BlendedWordRecall,String>("Recall Date");
        recallDateCol.setMinWidth(230);
        recallDateCol.setCellValueFactory(cellData -> {
            LocalDateTime ldt = LocalDateTime.ofInstant(cellData.getValue().getDateOfRecall().toInstant(),ZoneId.systemDefault());
            return new SimpleStringProperty(ldt.format(dateTimeFormat));
        });
        
        TableColumn<BlendedWordRecall,String> correctCol = new TableColumn<BlendedWordRecall,String>("Correct?");
        correctCol.setMinWidth(230);
        correctCol.setCellValueFactory(cellData -> {
            String booleanString = cellData.getValue().isWasCorrect() == true ? "Correct" : "Incorrect";
            return new SimpleStringProperty(booleanString);
        });
        correctCol.setCellFactory(column -> {
            return new TableCell<BlendedWordRecall,String>() {
                @Override
                protected void updateItem(String item,boolean empty) {
                    super.updateItem(item,empty);
        
                    if (item == null || empty) {
                        setText(null);
                        setStyle("");
                    } else {
                        if (item.equals("Correct")) {
                            setText("Correct");
                            setStyle("  -fx-font-size: 14px;\r\n" + 
                                            "   -fx-font-family: \"Comic Sans MS\";\r\n" + 
                                            "   -fx-font-weight: bolder;\r\n" + 
                                            "    -fx-text-fill: #37ff00;");
                        } else {
                            setText("Incorrect");
                            setStyle("  -fx-font-size: 14px;\r\n" + 
                                            "   -fx-font-family: \"Comic Sans MS\";\r\n" + 
                                            "   -fx-font-weight: bolder;\r\n" + 
                                            "    -fx-text-fill: #E60000;");
                        }
                    }
                }
            };
        });
        
        TableColumn<BlendedWordRecall,String> sightCol = new TableColumn<BlendedWordRecall,String>("Sight Read?");
        sightCol.setMinWidth(150);
        sightCol.setCellValueFactory(cellData -> {
            String sightString = cellData.getValue().isWasSightRead() == true ? "True" : "False";
            return new SimpleStringProperty(sightString);
        });
        
        TableColumn<BlendedWordRecall,String> recallTimeCol = new TableColumn<BlendedWordRecall,String>("Recall Time");
        recallTimeCol.setMinWidth(150);
        recallTimeCol.setCellValueFactory(cellData -> {
            String timeString = String.format("%.2f",cellData.getValue().getRecallDuration())+" Sec";
            return new SimpleStringProperty(timeString);
        });

        TableColumn<BlendedWordRecall,String> blendsCol = new TableColumn<BlendedWordRecall,String>("Blends");
        blendsCol.setMinWidth(300);
       
        blendsCol.setCellValueFactory(cellData -> {
            return new SimpleStringProperty("yes");// Set just so the cell.itemProperty will fire all the time
        });
         blendsCol.setCellFactory(col -> {
             
            TableCell<BlendedWordRecall,String> cell = new TableCell<>();
            
            cell.itemProperty().addListener((obs,old,newVal) -> {
                if (newVal != null) {
                    //////////////////////////////// Create nested table //////////////////////////////////////
                    ObservableList<BlendClass> blendList = FXCollections.observableArrayList();
                    SingleLetterBlendRecall slbOne = new SingleLetterBlendRecall("a / A","A","b / B","B");
                    SingleLetterBlendRecall slbTwo = new SingleLetterBlendRecall("b / B","B","c / C","C");
                    blendList.addAll(slbOne,slbTwo);
                    TableView<BlendClass> blendTable = new TableView<BlendClass>();
                    
                    blendTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
                    TableColumn<BlendClass,String> firstCol = new TableColumn<BlendClass,String>("First Sound");
                    firstCol.setMinWidth(150);
                    firstCol.setCellValueFactory(cellData -> {
                        return new SimpleStringProperty(cellData.getValue().getFirstSoundName());
                    });
                    
                    TableColumn<BlendClass,String> secondCol = new TableColumn<BlendClass,String>("Second Sound");
                    secondCol.setMinWidth(150);
                    secondCol.setCellValueFactory(cellData -> {
                        return new SimpleStringProperty(cellData.getValue().getSecondSoundName());
                    });
                    blendTable.getColumns().addAll(firstCol,secondCol);
                    blendTable.setItems(blendList);
                    blendTable.setMaxHeight(40 + (30*blendList.size()));
                    blendTable.setMinHeight(40 + (30*blendList.size()));
                    blendTable.setPrefHeight(40 + (30*blendList.size()));
                    ///////////////////////////////////////////////////////////////////////////////////
                    cell.graphicProperty().bind(Bindings.when(cell.emptyProperty()).then((Node) null).otherwise(blendTable));
                   
            }
        });
        return cell;
    });
        
        table.getColumns().addAll(wordCol,recallDateCol,correctCol,sightCol,recallTimeCol,blendsCol);
        table.setItems(data);
        
        
    return table;
}