如何设置单元格背景颜色(JavaFX、tableview) [英] How to set cell background color (JavaFX, tableview)

查看:184
本文介绍了如何设置单元格背景颜色(JavaFX、tableview)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have TableView which is used for displaying data made from CostDataRow objects. The most of cells in table view are editable and holds integer data so here is excerpt how I initialize the table and one of its editable numeric columns:

// `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();
        }
    }
);

现在我需要在用户单击一个工具栏的按钮后为所选单元格设置背景颜色 - 这是操作的处理程序:

Now I need to set background color on selected cells after user clicked on one toolbar's button - here is the action's handler:

@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.
        // ...
    });
}

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

Colors should be saved/loaded from same data model as data self.

感谢您的帮助!

推荐答案

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

In my experience/opinion, the trick to anything like this is properly representing the data you need in the model. In this case, it looks like you need some (or perhaps all) of the properties in the table model to carry an associated attribute. I would create a specific class for those properties that carry the attribute. For example, if you are wanting the user to be able to verify values in the table, you basically want each cell in the table to show both the value and the current "verification state" of that value. Hence you need a class that encapsulates a value and a verification state, which should be observable. So you would do something like:

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>weightColumn,你可能会这样做:

Now create table cells that observe the verification state of the current item in the table. So for example, given a TableColumn<Product, VerifiableValue<Double>> weightColumn, you might do:

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 来表示单元格的视图状态",并使用外部样式表将这些状态实际映射到颜色.

Here's a SSCCE. I moved the most important parts to the top of the code (so things are in an unusual order), and move the creation of the table cell to a method to reduce repetitive code. In real life I would probably roll my own table cell for this, so the labels display "Kg" but they don't appear in the text field, and use a text formatter on the text field to prevent invalid input. I would also move the style out of the cell implementation code, and use CSS PseudoClasses to represent the "view state" of the cell, and an external style sheet to actually map those states to colors.

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);
    }
}

这篇关于如何设置单元格背景颜色(JavaFX、tableview)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆