JavaFX:TableView里面的Dialog有重复的项目 [英] JavaFX : TableView inside Dialog has duplicate items

查看:416
本文介绍了JavaFX:TableView里面的Dialog有重复的项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个问题,我的 TableView 及其项目。我创建了一个小的对话框窗口来显示有关我的应用程序的警告,而在对话框中我有一个 TableView ,显示警告的名称和点击按钮的一些信息。

I have an issue with my TableView and its items. I have created a small Dialog window to display warnings about my app, and inside the Dialog I have a TableView which displays the name of the warning and some information about it upon clicking on a button.

我创建了一个 WarningUtil class( Singleton pattern)只是打开/关闭对话框 。相关代码如下:

I have created a WarningUtil class (Singleton pattern) just to open / close the Dialog. The relevant code follows.

WarningUtil 类的构造函数(仅调用一次):

The constructor of the WarningUtil class (called once only) :

private WarningUtil(RootCtrl rootCtrl) {
    this.rootCtrl = rootCtrl;
    warnings = new HashMap<>();

    setupWarningCallbacks(); // not relevant
    setupTable();
    setupColumns(); // not relevant
    setupDialog();
}

管理对话框 code>

private void setupTable() {
    // create the content pane
    content = new AnchorPane(); // class variable - reference needed for further uses
    content.setPrefSize(480, 240);

    // create the root nodes of the view (table + 2 columns)
    warningTable = new TableView<>(); // class variable - reference needed for further uses
    warnDescriptionCol = new PTableColumn<>(); // class variable - reference needed for further uses
    warnDetailsCol = new PTableColumn<>(); // class variable - reference needed for further uses

    // settings anchors to keep the ration between dialog <-> table
    AnchorPane.setBottomAnchor(warningTable, 15.0);
    AnchorPane.setTopAnchor(warningTable, 15.0);
    AnchorPane.setLeftAnchor(warningTable, 15.0);
    AnchorPane.setRightAnchor(warningTable, 15.0);

    // setting up the columns
    warnDescriptionCol.setText(i18n("label.desc"));
    warnDetailsCol.setText(i18n("label.details"));
    warnDescriptionCol.setPercentageWidth(0.7);
    warnDetailsCol.setPercentageWidth(0.3);
    warnDescriptionCol.setResizable(false);
    warnDetailsCol.setResizable(false);

    // adding nodes to containers
    warningTable.getColumns().addAll(warnDescriptionCol, warnDetailsCol);
    content.getChildren().add(warningTable);
}

用于创建对话框并设置内容

private void setupDialog() {
    // creation and saving of the dialog in a variable reused later
    warningDialog = DialogFactory.getInstance(rootCtrl.getPrimaryStage()).createWarningDialog();
    warningDialog.getDialogPane().setContent(content);
    warningDialog.getDialogPane().getScene().getWindow().sizeToScene();
}

// The DialogFactory function creating the dialog

public Dialog createWarningDialog(){
    CustomDialog dialog = new CustomDialog(rootStage);

    dialog.setTitle(i18n("warning.description"));

    ButtonType cancelBt = new ButtonType(i18n("button.close"), ButtonData.OK_DONE);
    dialog.getDialogPane().getButtonTypes().add(cancelBt);

    return dialog.setupLayout();
}

主要负责加载警告(存储在 .json 文件中,并在启动应用程序时反序列化)。现在,文件只包含一个条目

The Main class is in charge of loading the warnings (stored in a .json file and deserialized upon starting the app). For now, the file only contains one entry.

当我点击我的警告按钮时,调用以下函数:

When I click on my Warning button, the following function is called :

public void showWarnings() {
    warningTable.getItems().clear(); // BP
    warningTable.setItems(FXCollections.observableArrayList(warnings.values()));
    warningDialog.showAndWait();
}

发生以下情况:当我的.json中只有一个条目文件,我第一次点击按钮,只显示一个警告。如果我再次点击,第二个条目(相同)不应该可能是因为以下原因:

What happens is the following : When I have only one entry in my .json file, the first time I click on the button, only one warning is shown. If I click a second time, a second entry appears (the same) which should not be possible because of the following reasons :


  • 逻辑约束: warnings.values()来自 HashMap 其中键是警告的类型( WarningType class)>不可能有两个相同键

  • 调试:当我在// BP中设置断点时,我清楚地看到 warningTable 有一个项目,清零后的项目数量为零

  • 调试:仍然有相同的断点,我还检查 warnings.values()有只有一个项目,就是这样的情况

  • Logic constraint : warnings.values() comes from an HashMap where the key is the type of the warning (WarningType class) > Not possible to have two identical keys
  • Debugging : When I set a breakpoint at "//BP", I clearly see that the warningTable has one item, and after clear the number of items is zero
  • Debugging : Still with the same breakpoint, I also check that warnings.values() has only one item, which is the case

点击按钮五次后, 对话框清楚地显示出一些错误

After five clicks on the button, the Dialog clearly shows something is bugging.

更令人惊讶的是,当我添加第二个警告(不同于第一个,另一个类型),问题不会发生:没有重复,警告正确显示,无论打开窗口多少次。

More surprisingly, when I add a second warning (different from the first one, another type), the problem does not occur : No duplicates, warnings are correctly displayed and no matter how many times I open the window.

我的问题是:可以这样,我创建这个警告对话框的方式会导致不常见的错误?如果是这样,为什么不会出现两个警告?

My question is : Could that be that the way I am creating this warning dialog leads to uncommon errors ? If so, why isn't it the case with two warnings ?

编辑包含 cellFactories / cellValueFactories

private void setupColumns() {
    warnDescriptionCol.setCellFactory(new Callback<TableColumn<CustomWarning, String>, TableCell<CustomWarning, String>>() {
        @Override
        public TableCell<CustomWarning, String> call(TableColumn<CustomWarning, String> param) {
            TableCell<CustomWarning, String> cell = new TableCell<CustomWarning, String>() {
                @Override
                protected void updateItem(String item, boolean empty) {
                    super.updateItem(item, empty);

                    if (item != null) {
                        Label label = new Label(item);
                        setGraphic(label);
                    }
                }
            };

            return cell;
        }
    });

    warnDetailsCol.setCellFactory(new Callback<TableColumn<CustomWarning, CustomWarning>, TableCell<CustomWarning, CustomWarning>>() {
        @Override
        public TableCell<CustomWarning, CustomWarning> call(TableColumn<CustomWarning, CustomWarning> param) {
            TableCell<CustomWarning, CustomWarning> cell = new TableCell<CustomWarning, CustomWarning>() {
                @Override
                protected void updateItem(CustomWarning item, boolean empty) {
                    super.updateItem(item, empty);

                    if (item != null) {
                        Button button = new Button(i18n("button.view"));
                        button.getStyleClass().add("save");
                        button.setOnAction(new EventHandler<ActionEvent>() {
                            @Override
                            public void handle(ActionEvent event) {
                                showWarning(item);
                            }
                        });

                        setGraphic(button);
                    }
                }
            };

            return cell;
        }
    });

    warnDescriptionCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<CustomWarning, String>, ObservableValue<String>>() {
        TableViewObjectWrapper<CustomWarning, String> wrapper = new TableViewObjectWrapper<CustomWarning, String>() {
            @Override
            public String getData() {
                return getModel().getTitle();
            }
        };

        @Override
        public ObservableValue<String> call(TableColumn.CellDataFeatures<CustomWarning, String> param) {
            return new ReadOnlyObjectWrapper<>(wrapper.setModel(param.getValue()).getData());
        }
    });

    warnDetailsCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<CustomWarning, CustomWarning>, ObservableValue<CustomWarning>>() {
        TableViewObjectWrapper<CustomWarning, CustomWarning> wrapper = new TableViewObjectWrapper<CustomWarning, CustomWarning>() {
            @Override
            public CustomWarning getData() {
                return getModel();
            }
        };

        @Override
        public ObservableValue<CustomWarning> call(TableColumn.CellDataFeatures<CustomWarning, CustomWarning> param) {
            return new ReadOnlyObjectWrapper<>(wrapper.setModel(param.getValue()).getData());
        }
    });
}


推荐答案

你必须清除你的单元格在单元格工厂中,如果单元格为空,如文档

You have to clear your cells in the cell factory if the cell is empty, as explained in the documentation:


Cell的子类覆盖updateItem方法非常重要正确地,如果不这样做将导致诸如空白单元格或其中出现意想不到的内容的单元格等问题。以下是如何正确覆盖updateItem方法的示例:

It is very important that subclasses of Cell override the updateItem method properly, as failure to do so will lead to issues such as blank cells or cells with unexpected content appearing within them. Here is an example of how to properly override the updateItem method:



 protected void updateItem(T item, boolean empty) {
     super.updateItem(item, empty);

     if (empty || item == null) {
         setText(null);
         setGraphic(null);
     } else {
         setText(item.toString());
     }
 }




在此代码中注意示例两个重点:

Note in this code sample two important points:


  1. 我们调用super.updateItem(T,boolean)方法。如果没有这样做,项目和空属性设置不正确,您可能会遇到图形问题。

  2. 我们测试空条件,如果为true,我们将文本和图形属性设置为null。如果我们不这样做,几乎可以保证最终用户意外地看到细胞中的图形工件。


由于单元格被重复使用,您必须清除图形,如果它变空,只是设置它,如果不是。

Since the cells are reused, you have to clear the graphic if it has become empty, not just set it if it's not.

这篇关于JavaFX:TableView里面的Dialog有重复的项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆