Java 使用Observable更新TableView中的行颜色

Java 使用Observable更新TableView中的行颜色,java,javafx,fxml,Java,Javafx,Fxml,当A和B列值相等时,我想将行颜色更改为绿色,如果它们不同,则将行颜色更改为红色。我完全不知道如何改编在SO上找到的例子。我添加了监听器?使用可观察的?我添加了为进一步修改而准备的全部代码 sample.fxml: GridPane fx:controller="sample.Controller" xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10"> <TableVie

当A和B列值相等时,我想将行颜色更改为绿色,如果它们不同,则将行颜色更改为红色。我完全不知道如何改编在SO上找到的例子。我添加了监听器?使用可观察的
?我添加了为进一步修改而准备的全部代码

sample.fxml:

GridPane fx:controller="sample.Controller"
      xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">

    <TableView fx:id="tableView" GridPane.columnIndex="0" GridPane.rowIndex="0" editable="true">
        <columns>
            <TableColumn fx:id="colA" text="A">
                <cellValueFactory>
                    <PropertyValueFactory property="A"/>
                </cellValueFactory>
            </TableColumn>

            <TableColumn fx:id="colB" text="B">
                <cellValueFactory>
                    <PropertyValueFactory property="B"/>
                </cellValueFactory>
            </TableColumn>
        </columns>
        <items>
            <FXCollections fx:factory="observableArrayList"/>
        </items>
    </TableView>
</GridPane>
Controller.java:

public class Controller implements Initializable {

    @FXML
    private TableView<RowTest> tableView;
    @FXML
    private TableColumn<RowTest, Integer> colB;
    @FXML
    private ObservableList<RowTest> data;


    @Override
    public void initialize(URL location, ResourceBundle resources) {

        data = FXCollections.observableArrayList();

        populateData();
        setColumnsEditable();
    }

    private void setColumnsEditable() {
        tableView.setRowFactory(tv -> new TableRow<RowTest>() {
            @Override
            public void updateItem(RowTest item, boolean empty) {
                super.updateItem(item, empty);
                if (item == null) {
                    setStyle("");
                } else if (item.isValidRow()) {
                    setStyle("-fx-background-color: LightGreen; -fx-text-fill: Black;");
                } else if (!item.isValidRow()) {
                    setStyle("-fx-background-color: Red; -fx-text-fill: Black;");
                } else {
                    setStyle("");
                }
            }
        });

        colB.setCellFactory(TextFieldTableCellAutoCmt.forTableColumn(new IntToStringConverter<Integer>()));
        colB.setOnEditCommit(
                (TableColumn.CellEditEvent<RowTest, Integer> t) -> {
                    ((RowTest) t.getTableView().getItems().get(
                            t.getTablePosition().getRow())
                    ).setB(t.getNewValue());
                });

        data.addListener(new ListChangeListener<RowTest>() {
            @Override
            public void onChanged(Change<? extends RowTest> c) {
                while (c.next()) {
                    if (c.wasUpdated()) {

                   }
                }
            }
        });
    }

    private void populateData() {
        data.add(new RowTest(1, 1));
        data.add(new RowTest(1, 0));

        tableView.setItems(data);
    }
}
TextFieldTableCellAutoCmt.java:

public class TextFieldTableCellAutoCmt<S, T> extends TextFieldTableCell<S, T> {

    protected TextField textField;
    protected boolean isEdit;

    public TextFieldTableCellAutoCmt() {
        this(null);
    }

    public TextFieldTableCellAutoCmt(final StringConverter<T> conv) {
        super(conv);
    }

    public static <S> Callback<TableColumn<S, String>, TableCell<S, String>> forTableColumn() {
        return forTableColumn(new DefaultStringConverter());
    }

    public static <S, T> Callback<TableColumn<S, T>, TableCell<S, T>> forTableColumn(final StringConverter<T> conv) {
        return list -> new TextFieldTableCellAutoCmt<S, T>(conv);
    }

    @Override
    public void startEdit() {
        super.startEdit();
        isEdit = true;
        if (updateTextField()) {
            textField.focusedProperty().addListener(this::onFocusChange);
            textField.setOnKeyPressed(this::onKeyPress);
        }
    }

    /**
     * @return whether {@link #textField} has been changed
     */
    protected boolean updateTextField() {
        final Node g = getGraphic();
        final boolean isUpd = g != null && textField != g;
        if (isUpd) {
            textField = g instanceof TextField ? (TextField) g : null;
        }
        return isUpd;
    }

    @Override
    public void commitEdit(final T valNew) {
        if (isEditing()) {
            super.commitEdit(valNew);
        } else {
            final TableView<S> tbl = getTableView();
            if (tbl != null) {
                final TablePosition<S, T> pos = new TablePosition<>(tbl, getTableRow().getIndex(), getTableColumn()); // instead of tbl.getEditingCell()
                final TableColumn.CellEditEvent<S, T> ev = new TableColumn.CellEditEvent<>(tbl, pos, TableColumn.editCommitEvent(), valNew);
                Event.fireEvent(getTableColumn(), ev);
            }
            updateItem(valNew, false);
            if (tbl != null) {
                tbl.edit(-1, null);
            }
            // TODO ControlUtils.requestFocusOnControlOnlyIfCurrentFocusOwnerIsChild(tbl);
        }
    }

    public void onFocusChange(final ObservableValue<? extends Boolean> obs, final boolean v0, final boolean v1) {
        if (isEdit && !v1) {
            commitEdit(getConverter().fromString(textField.getText()));
        }
    }

    protected void onKeyPress(final KeyEvent e) {
        switch (e.getCode()) {
            case ESCAPE:
                isEdit = false;
                cancelEdit(); // see CellUtils#createTextField(...)
                e.consume();
                break;
            case TAB:
                if (e.isShiftDown()) {
                    getTableView().getSelectionModel().selectPrevious();
                } else {
                    getTableView().getSelectionModel().selectNext();
                }
                e.consume();
                break;
            case UP:
                getTableView().getSelectionModel().selectAboveCell();
                e.consume();
                break;
            case DOWN:
                getTableView().getSelectionModel().selectBelowCell();
                e.consume();
                break;
            default:
                break;
        }
    }
}
我改为:

this.validRow.bind(Bindings.createBooleanBinding(() -> this.a.get() == this.b.get(), this.aProperty(), this.bProperty()));

这解决了我的问题,但我不确定这是否是一个正确的解决方案

data = FXCollections.observableArrayList(e -> new Observable[]{e.validRowProperty()});
在此处找到并修改了此代码:


请注意M:-)原始项目将列的总和与其他列的总和进行比较,我删除了所有可以删除的内容。这是很多代码,
TextFieldTableCellAutoCmt.java
不是我的,但我也发布了它来展示单元格是如何编辑的。我试图分析这么多示例,your和James_D,但我不知道如何做到这一点。是的,我可以删除一个
CellFactory
OnEditCommit
,无需在两列上都使用它。但这是一个次要的代码容量。。。如果你这么说*ShrugsOK,您的绑定是错误的:您需要将a和b作为依赖项传递,然后使用validRow上的提取器实例化这些项,然后。。。你在tableRow中遇到了一个bug,它没有被通知(或没有对更新类型的更改做出反应)-看看关于切换行状态的其他问题,有一些解决方法(sry,现在必须运行,稍后会尝试回来;)很好,这是可能的,并且很好地涵盖了大多数(可能所有)用例:)个人,我不喜欢将单元格属性绑定到项目属性(在某些情况下,它们可能无法正确清理)-但这只是我的偏好..你能在空闲时间展示其他解决方案吗?更优雅的?优雅是主观的:)另一种选择(在提取器就位的情况下)是覆盖tableRow的isItemChanged以返回true,前提是旧的和新的相同,并且它的有效属性不同。没有时间给出一个好的答案——但如果你敢看的话,可以尝试一下
public class TextFieldTableCellAutoCmt<S, T> extends TextFieldTableCell<S, T> {

    protected TextField textField;
    protected boolean isEdit;

    public TextFieldTableCellAutoCmt() {
        this(null);
    }

    public TextFieldTableCellAutoCmt(final StringConverter<T> conv) {
        super(conv);
    }

    public static <S> Callback<TableColumn<S, String>, TableCell<S, String>> forTableColumn() {
        return forTableColumn(new DefaultStringConverter());
    }

    public static <S, T> Callback<TableColumn<S, T>, TableCell<S, T>> forTableColumn(final StringConverter<T> conv) {
        return list -> new TextFieldTableCellAutoCmt<S, T>(conv);
    }

    @Override
    public void startEdit() {
        super.startEdit();
        isEdit = true;
        if (updateTextField()) {
            textField.focusedProperty().addListener(this::onFocusChange);
            textField.setOnKeyPressed(this::onKeyPress);
        }
    }

    /**
     * @return whether {@link #textField} has been changed
     */
    protected boolean updateTextField() {
        final Node g = getGraphic();
        final boolean isUpd = g != null && textField != g;
        if (isUpd) {
            textField = g instanceof TextField ? (TextField) g : null;
        }
        return isUpd;
    }

    @Override
    public void commitEdit(final T valNew) {
        if (isEditing()) {
            super.commitEdit(valNew);
        } else {
            final TableView<S> tbl = getTableView();
            if (tbl != null) {
                final TablePosition<S, T> pos = new TablePosition<>(tbl, getTableRow().getIndex(), getTableColumn()); // instead of tbl.getEditingCell()
                final TableColumn.CellEditEvent<S, T> ev = new TableColumn.CellEditEvent<>(tbl, pos, TableColumn.editCommitEvent(), valNew);
                Event.fireEvent(getTableColumn(), ev);
            }
            updateItem(valNew, false);
            if (tbl != null) {
                tbl.edit(-1, null);
            }
            // TODO ControlUtils.requestFocusOnControlOnlyIfCurrentFocusOwnerIsChild(tbl);
        }
    }

    public void onFocusChange(final ObservableValue<? extends Boolean> obs, final boolean v0, final boolean v1) {
        if (isEdit && !v1) {
            commitEdit(getConverter().fromString(textField.getText()));
        }
    }

    protected void onKeyPress(final KeyEvent e) {
        switch (e.getCode()) {
            case ESCAPE:
                isEdit = false;
                cancelEdit(); // see CellUtils#createTextField(...)
                e.consume();
                break;
            case TAB:
                if (e.isShiftDown()) {
                    getTableView().getSelectionModel().selectPrevious();
                } else {
                    getTableView().getSelectionModel().selectNext();
                }
                e.consume();
                break;
            case UP:
                getTableView().getSelectionModel().selectAboveCell();
                e.consume();
                break;
            case DOWN:
                getTableView().getSelectionModel().selectBelowCell();
                e.consume();
                break;
            default:
                break;
        }
    }
}
data.addListener(new ListChangeListener<RowTest>() {
    @Override
    public void onChanged(Change<? extends RowTest> c) {
        while (c.next()) {
            if (c.wasUpdated()) {

            }
        }
    }
});
 this.validRow.bind(Bindings.createBooleanBinding(() -> this.a.get() == this.b.get()));
this.validRow.bind(Bindings.createBooleanBinding(() -> this.a.get() == this.b.get(), this.aProperty(), this.bProperty()));
data = FXCollections.observableArrayList(e -> new Observable[]{e.validRowProperty()});
tableView.setRowFactory(tv -> new TableRow<RowTest>() {
    @Override
    protected void updateItem(final RowTest item, final boolean empty) {
        super.updateItem(item, empty);

        if (!empty && item != null) {
            this.styleProperty().bind(Bindings.createStringBinding(() -> {
                if (item.isValidRow()) {
                    return "-fx-background-color: green;";
                } else {
                    return "-fx-background-color: red;";
                }
            }, item.validRowProperty()));
        } else {
            setText(null);
            setGraphic(null);

            this.styleProperty().unbind();

            setStyle(" ");
        }
    }
});
this.validRow.bind(Bindings.createBooleanBinding(() -> 
    this.a.get() == this.b.get(), this.aProperty(), this.bProperty()));