我用TableView
它来显示从CostDataRow
对象产生的数据.表视图中的大多数单元格都是可编辑的,并且包含整数数据,所以这里摘录了我如何初始化表格及其可编辑的数字列之一:
// `data` is `ObservableList` made by // using `FXCollections. observableArrayList(...)`. FilteredList filteredData = new FilteredList<>(data, n -> true); table.setEditable(true); table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); table.getSelectionModel().setCellSelectionEnabled(true); table.setItems(filteredData); // ... // Below is "wireWeight" column (Integer data) wireWeightTCol.setCellValueFactory(new PropertyValueFactory<>("wireWeight")); wireWeightTCol.setCellFactory(TextFieldTableCell. forTableColumn(new StringConverter () { @Override public String toString(final Integer value) { return value.toString() + " kg"; } @Override public Integer fromString(final String s) { return Integer.parseInt(s.replace(" kg", "")); } })); wireWeightTCol.setOnEditCommit( new EventHandler >() { @Override public void handle(CellEditEvent t) { CostDataRow row = (CostDataRow) t.getTableView().getItems().get(t.getTablePosition().getRow()); int weight = t.getNewValue(); row.setWireWeight(t.getNewValue()); // Calculate wire price row.setWirePrice((int)(weight * getWirePriceConstant())); // Refresh table view t.getTableView().refresh(); } } );
现在,我需要在用户点击一个工具栏的按钮后在所选单元格上设置背景颜色 - 这是动作的处理程序:
@FXML private void handleYellowButtonAction(ActionEvent event) { table.getSelectionModel().getSelectedCells().forEach(pos -> { int row = pos.getRow(); int col = pos.getColumn(); // TODO Now I need to set background color of cell with // given row and column index. // ... }); }
应该从与数据自身相同的数据模型中保存/加载颜色.
感谢帮助!
根据我的经验/意见,这样的技巧就是正确地表示模型中需要的数据.在这种情况下,看起来您需要表模型中的某些(或可能是所有)属性来携带关联的属性.我会为那些带有属性的属性创建一个特定的类.例如,如果您希望用户能够验证表中的值,您基本上希望表中的每个单元都显示该值的值和当前"验证状态".因此,您需要一个封装值和验证状态的类,该类应该是可观察的.所以你会做类似的事情:
public class VerifiableValue{ public enum VerificationState { UNVERIFIED, VERIFIED, ERROR } private final T value ; private final ObjectProperty verificationState = new SimpleObjectProperty<>(VerificationState.UNVERIFIED) ; public VerifiableValue(T value) { this.value = value ; } public VerifiableValue(T value, VerificationState verificationState) { this(value); setVerificationState(verificationState); } public T getValue() { return value ; } public final ObjectProperty verificationStateProperty() { return this.verificationState; } public final VerifiableValue.VerificationState getVerificationState() { return this.verificationStateProperty().get(); } public final void setVerificationState( final VerifiableValue.VerificationState verificationState) { this.verificationStateProperty().set(verificationState); } }
现在创建表格单元格,观察表格中当前项目的验证状态.例如,给定a TableColumn
,您可能会这样做:
weightColumn.setCellFactory(tc -> { TextFieldTableCell> cell = new TextFieldTableCell<>(); cell.setConverter(new StringConverter >() { @Override public String toString(VerifiableValue item) { return item == null ? "" : String.format("%.3f Kg", item.getValue()); } @Override public VerifiableValue fromString(String string) { T value = new Double(string.replace("Kg","").trim()); VerifiableValue.VerificationState verificationState = cell.getItem() == null ? VerifiableValue.VerificationState.UNVERIFIED : cell.getItem().getVerificationState() ; return new VerifiableValue<>(value, verificationState); } }); ChangeListener verifiedListener = (obs, oldState, newState) -> { if (newState == null || newState == VerifiableValue.VerificationState.UNVERIFIED) { cell.setStyle(""); } else if (newState == VerifiableValue.VerificationState.VERIFIED) { cell.setStyle("-fx-background-color: yellow ;"); } else if (newState == VerifiableValue.VerificationState.ERROR) { cell.setStyle("-fx-background-color: red ;"); } }; cell.itemProperty().addListener((obs, oldItem, newItem) -> { if (oldItem != null) { oldItem.verificationStateProperty().removeListener(verifiedListener); } if (newItem == null) { cell.setStyle(""); } else { if (newItem.getVerificationState() == null || newItem.getVerificationState() == VerifiableValue.VerificationState.UNVERIFIED) { cell.setStyle(""); } else if (newItem.getVerificationState() == VerifiableValue.VerificationState.VERIFIED) { cell.setStyle("-fx-background-color: yellow ;"); } else if (newItem.getVerificationState() == VerifiableValue.VerificationState.ERROR) { cell.setStyle("-fx-background-color: red ;"); } newItem.verificationStateProperty().addListener(verifiedListener); } }); return cell ; });
这是一个SSCCE.我将最重要的部分移动到代码的顶部(因此事情处于不寻常的顺序),并将表格单元的创建移动到减少重复代码的方法.在现实生活中,我可能会为此滚动我自己的表格单元格,因此标签显示"Kg"但它们不会出现在文本字段中,并在文本字段上使用文本格式化程序来防止无效输入.我还将样式移出单元格实现代码,并使用CSS PseudoClasses来表示单元格的"视图状态",并使用外部样式表将这些状态实际映射到颜色.
import java.util.Random; import java.util.function.Function; import javafx.application.Application; import javafx.beans.binding.Bindings; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.value.ChangeListener; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.SelectionMode; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TablePosition; import javafx.scene.control.TableView; import javafx.scene.control.cell.TextFieldTableCell; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.stage.Stage; import javafx.util.StringConverter; public class CellHighlightingTable extends Application { public static class VerifiableValue{ public enum VerificationState { UNVERIFIED, VERIFIED, ERROR } private final T value ; private final ObjectProperty verificationState = new SimpleObjectProperty<>(VerificationState.UNVERIFIED) ; public VerifiableValue(T value) { this.value = value ; } public VerifiableValue(T value, VerificationState verificationState) { this(value); setVerificationState(verificationState); } public T getValue() { return value ; } public final ObjectProperty verificationStateProperty() { return this.verificationState; } public final CellHighlightingTable.VerifiableValue.VerificationState getVerificationState() { return this.verificationStateProperty().get(); } public final void setVerificationState( final CellHighlightingTable.VerifiableValue.VerificationState verificationState) { this.verificationStateProperty().set(verificationState); } } private TableCell > createTableCell(String format, Function supplier) { TextFieldTableCell > cell = new TextFieldTableCell<>(); cell.setConverter(new StringConverter >() { @Override public String toString(VerifiableValue item) { return item == null ? "" : String.format(format, item.getValue()); } @Override public VerifiableValue fromString(String string) { T value = supplier.apply(string); VerifiableValue.VerificationState verificationState = cell.getItem() == null ? VerifiableValue.VerificationState.UNVERIFIED : cell.getItem().getVerificationState() ; return new VerifiableValue<>(value, verificationState); } }); ChangeListener verifiedListener = (obs, oldState, newState) -> { if (newState == null || newState == VerifiableValue.VerificationState.UNVERIFIED) { cell.setStyle(""); } else if (newState == VerifiableValue.VerificationState.VERIFIED) { cell.setStyle("-fx-background-color: yellow ;"); } else if (newState == VerifiableValue.VerificationState.ERROR) { cell.setStyle("-fx-background-color: red ;"); } }; cell.itemProperty().addListener((obs, oldItem, newItem) -> { if (oldItem != null) { oldItem.verificationStateProperty().removeListener(verifiedListener); } if (newItem == null) { cell.setStyle(""); } else { if (newItem.getVerificationState() == null || newItem.getVerificationState() == VerifiableValue.VerificationState.UNVERIFIED) { cell.setStyle(""); } else if (newItem.getVerificationState() == VerifiableValue.VerificationState.VERIFIED) { cell.setStyle("-fx-background-color: yellow ;"); } else if (newItem.getVerificationState() == VerifiableValue.VerificationState.ERROR) { cell.setStyle("-fx-background-color: red ;"); } newItem.verificationStateProperty().addListener(verifiedListener); } }); return cell ; } @Override public void start(Stage primaryStage) { TableView table = new TableView<>(); table.getSelectionModel().setCellSelectionEnabled(true); table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); table.setEditable(true); TableColumn productCol = new TableColumn<>("Product"); productCol.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getName())); TableColumn > quantityColumn = new TableColumn<>("Quantity"); quantityColumn.setCellValueFactory(cellData -> cellData.getValue().quantityProperty()); quantityColumn.setCellFactory(tc -> createTableCell("%,d", Integer::new)); TableColumn > weightColumn = new TableColumn<>("Weight"); weightColumn.setCellValueFactory(cellData -> cellData.getValue().weightProperty()); weightColumn.setCellFactory(tc -> createTableCell("%.3f Kg", Double::new)); table.getColumns().add(productCol); table.getColumns().add(quantityColumn); table.getColumns().add(weightColumn); Button verifySelected = new Button("Verify Selected"); verifySelected.setOnAction(e -> { for (TablePosition, ?> pos : table.getSelectionModel().getSelectedCells()) { if (pos.getTableColumn() == quantityColumn) { Product product = table.getItems().get(pos.getRow()); product.getQuantity().setVerificationState(VerifiableValue.VerificationState.VERIFIED); } else if (pos.getTableColumn() == weightColumn) { Product product = table.getItems().get(pos.getRow()); product.getWeight().setVerificationState(VerifiableValue.VerificationState.VERIFIED); } } }); verifySelected.disableProperty().bind(Bindings.isEmpty(table.getSelectionModel().getSelectedCells())); Button errorSelected = new Button("Mark all selected as error"); errorSelected.setOnAction(e -> { for (TablePosition, ?> pos : table.getSelectionModel().getSelectedCells()) { if (pos.getTableColumn() == quantityColumn) { Product product = table.getItems().get(pos.getRow()); product.getQuantity().setVerificationState(VerifiableValue.VerificationState.ERROR); } else if (pos.getTableColumn() == weightColumn) { Product product = table.getItems().get(pos.getRow()); product.getWeight().setVerificationState(VerifiableValue.VerificationState.ERROR); } } }); errorSelected.disableProperty().bind(Bindings.isEmpty(table.getSelectionModel().getSelectedCells())); Button unverifyAll = new Button("Remove all verification"); unverifyAll.setOnAction(e -> { for (Product product : table.getItems()) { product.getQuantity().setVerificationState(VerifiableValue.VerificationState.UNVERIFIED); product.getWeight().setVerificationState(VerifiableValue.VerificationState.UNVERIFIED); } }); HBox buttons = new HBox(5, verifySelected, errorSelected, unverifyAll); buttons.setAlignment(Pos.CENTER); buttons.setPadding(new Insets(5)); // random data: Random rng = new Random(); for (int i = 0 ; i < 100; i++) { Product product = new Product("Item "+(i+1)); product.setQuantity(new VerifiableValue<>(rng.nextInt(1200))); product.setWeight(new VerifiableValue<>(rng.nextDouble() * 1000)); table.getItems().add(product); } BorderPane root = new BorderPane(table); root.setBottom(buttons); Scene scene = new Scene(root); primaryStage.setScene(scene); primaryStage.show(); } public static class Product { private ObjectProperty > weight = new SimpleObjectProperty<>(); private ObjectProperty > quantity = new SimpleObjectProperty<>(); private final String name ; public Product(String name) { this.name = name ; } public String getName() { return name ; } public final ObjectProperty > weightProperty() { return this.weight; } public final CellHighlightingTable.VerifiableValue getWeight() { return this.weightProperty().get(); } public final void setWeight(final CellHighlightingTable.VerifiableValue weight) { this.weightProperty().set(weight); } public final ObjectProperty > quantityProperty() { return this.quantity; } public final CellHighlightingTable.VerifiableValue getQuantity() { return this.quantityProperty().get(); } public final void setQuantity(final CellHighlightingTable.VerifiableValue quantity) { this.quantityProperty().set(quantity); } } public static void main(String[] args) { launch(args); } }