如何设置单元格背景颜色(JavaFX,tableview)

时间:2016-12-28 13:10:51

标签: java javafx tableview javafx-8

我有TableView,用于显示从CostDataRow个对象生成的数据。表格视图中的大多数单元格都是可编辑的,并且包含整数数据,因此这里摘录了我如何初始化表格及其可编辑的数字列之一:

// `data` is `ObservableList<CostDataRow>` made by 
// using `FXCollections.<CostDataRow>observableArrayList(...)`.
FilteredList<CostDataRow> 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.<CostDataRow, Integer>forTableColumn(new StringConverter<Integer>() {
    @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<CellEditEvent<CostDataRow, Integer>>() {
        @Override
        public void handle(CellEditEvent<CostDataRow, Integer> 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.
        // ...
    });
}

应该从与数据自身相同的数据模型中保存/加载颜色。

感谢您的帮助!

4 个答案:

答案 0 :(得分:5)

根据我的经验/意见,这样的技巧就是正确地表示模型中需要的数据。在这种情况下,看起来您需要表模型中的某些(或可能是所有)属性来携带关联的属性。我会为那些带有属性的属性创建一个特定的类。例如,如果您希望用户能够验证表中的值,您基本上希望表中的每个单元都显示该值的值和当前“验证状态”。因此,您需要一个封装值和验证状态的类,该类应该是可观察的。所以你会做类似的事情:

public class VerifiableValue<T> {

    public enum VerificationState { UNVERIFIED, VERIFIED, ERROR }

    private final T value ;
    private final ObjectProperty<VerificationState> 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<VerificationState> 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);
    }


}

现在创建表格单元格,以观察表格中当前项目的验证状态。例如,给定TableColumn<Product, VerifiableValue<Double>> weightColumn,您可能会这样做:

weightColumn.setCellFactory(tc -> {
    TextFieldTableCell<Product, VerifiableValue<Double>> cell = new TextFieldTableCell<>();

    cell.setConverter(new StringConverter<VerifiableValue<Double>>() {

        @Override
        public String toString(VerifiableValue<Double> item) {
            return item == null ? "" : String.format("%.3f Kg", item.getValue());
        }

        @Override
        public VerifiableValue<T> 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<VerifiableValue.VerificationState> 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<T> {

        public enum VerificationState { UNVERIFIED, VERIFIED, ERROR }

        private final T value ;
        private final ObjectProperty<VerificationState> 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<VerificationState> 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 <T> TableCell<Product, VerifiableValue<T>> createTableCell(String format, Function<String, T> supplier) {
        TextFieldTableCell<Product, VerifiableValue<T>> cell = new TextFieldTableCell<>();

        cell.setConverter(new StringConverter<VerifiableValue<T>>() {

            @Override
            public String toString(VerifiableValue<T> item) {
                return item == null ? "" : String.format(format, item.getValue());
            }

            @Override
            public VerifiableValue<T> 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<VerifiableValue.VerificationState> 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<Product> table = new TableView<>();
        table.getSelectionModel().setCellSelectionEnabled(true);
        table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        table.setEditable(true);

        TableColumn<Product, String> productCol = new TableColumn<>("Product");
        productCol.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getName()));

        TableColumn<Product, VerifiableValue<Integer>> quantityColumn = new TableColumn<>("Quantity");
        quantityColumn.setCellValueFactory(cellData -> cellData.getValue().quantityProperty());

        quantityColumn.setCellFactory(tc -> createTableCell("%,d", Integer::new));

        TableColumn<Product, VerifiableValue<Double>> 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<VerifiableValue<Double>> weight = new SimpleObjectProperty<>();
        private ObjectProperty<VerifiableValue<Integer>> quantity = new SimpleObjectProperty<>();

        private final String name ;

        public Product(String name) {
            this.name = name ;
        }

        public String getName() {
            return name ;
        }

        public final ObjectProperty<VerifiableValue<Double>> weightProperty() {
            return this.weight;
        }

        public final CellHighlightingTable.VerifiableValue<java.lang.Double> getWeight() {
            return this.weightProperty().get();
        }

        public final void setWeight(final CellHighlightingTable.VerifiableValue<java.lang.Double> weight) {
            this.weightProperty().set(weight);
        }

        public final ObjectProperty<VerifiableValue<Integer>> quantityProperty() {
            return this.quantity;
        }

        public final CellHighlightingTable.VerifiableValue<java.lang.Integer> getQuantity() {
            return this.quantityProperty().get();
        }

        public final void setQuantity(final CellHighlightingTable.VerifiableValue<java.lang.Integer> quantity) {
            this.quantityProperty().set(quantity);
        }


    }

    public static void main(String[] args) {
        launch(args);
    }
}

答案 1 :(得分:0)

我浏览了一下,发现:

setStyle("-fx-background-color: x");

这是你想要做的吗?

答案 2 :(得分:0)

如果您有css,可以使用以下代码:

.table-row-cell:selected {
   -fx-background-color: yellow;
}

答案 3 :(得分:0)

无法从TableView中检索单元格实例。问题的解决方案是使用自定义TableCell类,并根据您指定的条件对其进行编程。

请注意,Cell实例可用于多个项目(例如,它们会被重复使用以提高性能)。

扩展TableCell类时,最重要的覆盖方法是updateItem()方法。以下是如何根据条件更改样式的示例。

   @Override
    protected void updateItem(LocalDate item, boolean empty) {
        super.updateItem(item, empty);

        if (item == null || empty) {
            setText(null);
            setStyle(""); // set cell style
        } else {
            if (checkCondition) {
                setTextFill(Color.CHOCOLATE);
                setStyle("-fx-background-color: red;");// set your css style here if condition is true
            } else {
                setTextFill(Color.BLACK);
                setStyle(""); // reser style if condition is false.
            }
        }
    }