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