Javafx - 使 TableView 单元格在编辑后更改样式

Javafx - Making a TableView Cell Change Style After Being Edited

提问人:Hexadelta 提问时间:11/14/2023 最后编辑:Hexadelta 更新时间:11/15/2023 访问量:64

问:

我试图在这里制作一个 TableView,当单元格的值被修改时(用户可以单击单元格并键入一个新值),如果方法 isValid 返回 false,则单元格会从样式表中获得无效单元格样式。

目前,编辑部分工作正常,isValid 根据控制台输出返回它应该返回的内容。但是,当 isValid 返回 false 时,单元格未获得 invalid-cell 属性。

我知道无效单元格样式是可访问的,因为已将样式添加到整个列中,但我无法做到只是一次更改样式的修改单元格。有什么想法吗?谢谢。column.getStyleClass().add("invalid-cell");

public void loadTable(String tableName) throws SQLException {
        //Clear existing columns and entries
        tableView.getColumns().clear();
        tableView.getItems().clear();

        ObservableList<String> data = DBcontroller.getColumns(handler.getTable());


        //For each column, add to table
        for (int i = 0; i < data.size(); i++) {
            int finalIdx = i;
            TableColumn<ObservableList<String>, String> column = new TableColumn<>(data.get(i));
            column.setPrefWidth(100); //Set width


            //Factory (gets column data)
            column.setCellValueFactory(param -> {
                String cellValue = param.getValue().get(finalIdx);
                return new SimpleStringProperty(cellValue);
            });


            //Make cells editable.
            column.setCellFactory(TextFieldTableCell.forTableColumn(new DefaultStringConverter()));
            column.setEditable(true);

            column.setOnEditCommit(event -> {
                ObservableList<String> rowData = event.getTableView().getItems().get(event.getTablePosition().getRow());
                rowData.set(finalIdx, event.getNewValue());


                TableCell<ObservableList<String>, String> currentCell = event.getTableColumn().getCellFactory().call(column);
                if (isValid(event.getNewValue(), column.getText())) {
                    System.out.println("Input OK!");
                    currentCell.getStyleClass().remove("invalid-cell");
                } else {
                    System.out.println("Input BAD");
                    currentCell.getStyleClass().add("invalid-cell");
                }
            });
            tableView.getColumns().add(column);
        }

目前使用包含的代码,即使 isValid 返回 false,也没有单元格获得样式。

爪哇 JavaFX

评论


答:

0赞 minus 11/14/2023 #1

在这种情况下,我通常做的是将逻辑放在 .cellFactory

        column.setCellFactory(x-> {
            TableCell<EventVW, Date> cell = new TableCell<EventVW, Date>() {
                @Override
                protected void updateItem(String item, boolean empty) {
                    super.updateItem(item, empty);
                    if (empty) {
                        setText(null);
                        ObservableList<String> styleClass = getStyleClass();
                        styleClass.remove("err-class"); 
                    } else {
                        setText(item);
                        styleClass.remove("err-class"); 
                        if(!isValid(item)) {
                            styleClass.add("err-class");    
                        }
                    }
                }
            };

因此,每当更新单元格值时,样式也会根据本例中的某个函数 () 进行更新isValid

0赞 DaveB 11/15/2023 #2

如果使用 PseudoClasses 完成,这是最干净的。此外,无论验证例程是什么,它都需要足够简单,可以在“视图”中完成,而不需要数据库查找或复杂的业务逻辑。

public class ValidatingTableCell<S> extends TableCell<S, String> {
    private static final PseudoClass redDisplay = PseudoClass.getPseudoClass("error");

    public ValidatingTableCell() {
        TextField textField = new TextField();
        textField.textProperty().bind(itemProperty());
        StackPane container = new StackPane(textField);
        container.getStyleClass().add("cell-box");
        container.setPadding(new Insets(8));
        itemProperty().addListener(itemProperty -> container.pseudoClassStateChanged(redDisplay, validateValue()));
        graphicProperty().bind(Bindings.createObjectBinding(() -> !isEmpty() ? container : null, emptyProperty()));
        setText(null);
    }

    private boolean validateValue() {
        String theValue = itemProperty().get();
        return theValue == null || theValue.startsWith("A");
    }
}

CSS 文件如下所示:

.cell-box {
   -fx-background-color: green;
}

.cell-box:error {
  -fx-background-color: red;
}

这种方法可能看起来有点奇怪,因为它不使用通常的方法,而是绑定到 ,并在创建布局的构造函数中执行所有连接。不过,结果是一样的。updateItem()itemProperty()CellCell

在此示例中,任何值以“A”开头的内容都将被视为无效,并且将在 .验证作为逻辑的一部分包含在内,以表明它确实需要简单才能包含在布局中。TextFieldCell

如果验证逻辑更复杂,则应将数据转换为数据和有效标志的包含字段。然后,应在业务逻辑中定义数据本身和有效标志之间的链接。CellObject

评论

0赞 Hexadelta 11/15/2023
干杯。与updateItem方法相比,使用此方法是否有任何好处(即性能方面)?
0赞 DaveB 11/16/2023
@Hexadelta我更愿意将 JavaFX 视为一个响应式框架。因此,动态行为的静态布局绑定到表示“状态”的某个数据模型。这种方法更适合这一点,因为您不会在事后摆弄布局中的任何节点。请注意,没有作为类的字段实现的节点,也没有在布局构造函数外部引用的节点。恕我直言,这是一种更清洁的方法。话虽如此,我从未在任何文档或在线教程中看到过这一点 - 无论这意味着什么。