当BooleanProperty在单独的TableView

时间:2017-03-22 21:44:36

标签: javafx treeview

在单独的TableView中满足条件时,使用新样式自动刷新TreeView的单元格的最简单方法是什么?

我目前正在设置TreeCells' TreeView单元工厂中的updateItem()方法中的样式,但只有在用户在TreeView中添加或删除某些内容时才会触发此样式。如果我在单独的对话框中勾选所有3个复选框,我希望能够更改给定TreeCell的样式。

我目前能够使用BooleanProperty和IntegerProperty来监控已检查复选框的数量,但我不知道我应该如何"自动更新"或者在TreeItem的对象的BooleanProperty更改时调用TreeView刷新。

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

只要更新TreeCell底层的值的布尔属性(通过绑定),就可以在TreeCell中设置样式。

sample

return new TreeCell<Message>() {
    @Override
    protected void updateItem(Message item, boolean empty) {
        super.updateItem(item, empty);

        styleProperty().unbind();

        if (empty || item == null || item.getText() == null) {
            setText(null);
            styleProperty.set(null);
        } else {
            setText(item.getText());
            styleProperty().bind(
                    Bindings.when(
                            item.readProperty()
                    ).then("-fx-background-color: red;")
                            .otherwise("-fx-background-color: null;")
            );
        }
    }
};

完整样本

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;

public class TreeViewSample extends Application {

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

    @Override
    public void start(Stage stage) {
        ObservableList<Message> messages = FXCollections.observableArrayList();

        TreeItem<Message> rootItem = new TreeItem<> (new Message("Inbox"));
        rootItem.setExpanded(true);
        for (int i = 1; i < 6; i++) {
            Message message = new Message("Message" + i);
            messages.add(message);
            TreeItem<Message> item = new TreeItem<> (message);
            rootItem.getChildren().add(item);
        }        
        TreeView<Message> tree = new TreeView<> (rootItem);
        tree.setCellFactory(new Callback<TreeView<Message>, TreeCell<Message>>() {
            @Override
            public TreeCell<Message> call(TreeView<Message> param) {
                return new TreeCell<Message>() {
                    @Override
                    protected void updateItem(Message item, boolean empty) {
                        super.updateItem(item, empty);

                        styleProperty().unbind();

                        if (empty || item == null || item.getText() == null) {
                            setText(null);
                            styleProperty.set(null);
                        } else {
                            setText(item.getText());
                            styleProperty().bind(
                                    Bindings.when(
                                            item.readProperty()
                                    ).then("-fx-background-color: red;")
                                            .otherwise("-fx-background-color: null;")
                            );
                        }
                    }
                };
            }
        });


        TableView<Message> tableView = new TableView<>();
        tableView.setEditable(true);

        TableColumn<Message, String> textCol = new TableColumn<>("Text");
        textCol.setCellValueFactory(new PropertyValueFactory<>("text"));
        tableView.getColumns().add(textCol);

        TableColumn<Message, Boolean> readCol = new TableColumn<>("Read");
        readCol.setCellValueFactory(new PropertyValueFactory<>("read"));
        readCol.setCellFactory(CheckBoxTableCell.forTableColumn(readCol));
        readCol.setEditable(true);
        tableView.getColumns().add(readCol);

        tableView.setItems(messages);

        VBox root = new VBox(10, tree, tableView);
        root.setPadding(new Insets(10));
        stage.setScene(new Scene(root, 300, 250));
        stage.show();
    }

    public class Message {
        private StringProperty text = new SimpleStringProperty();
        private BooleanProperty read = new SimpleBooleanProperty(false);

        public Message(String msgText) {
            text.set(msgText);
        }

        public String getText() {
            return text.get();
        }

        public StringProperty textProperty() {
            return text;
        }

        public void setText(String text) {
            this.text.set(text);
        }

        public boolean isRead() {
            return read.get();
        }

        public BooleanProperty readProperty() {
            return read;
        }

        public void setRead(boolean read) {
            this.read.set(read);
        }
    }
}
  

我正在尝试将graphicProperty绑定到相同的BooleanProperty并根据值更改图像。

使用与单元格关联的ImageView中的图像绑定的示例。

image binding

Image unreadImage = new Image("http://icons.iconarchive.com/icons/oxygen-icons.org/oxygen/16/Status-mail-unread-new-icon.png");
Image readImage = new Image("http://icons.iconarchive.com/icons/icons8/ios7/16/Messaging-Read-Message-icon.png");

. . . 

return new TreeCell<Message>() {
    ImageView imageView = new ImageView();

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

        styleProperty().unbind();
        imageView.imageProperty().unbind();

        if (empty || item == null || item.getText() == null) {
            setText(null);
            setGraphic(null);
            styleProperty().set(null);
        } else {
            setText(item.getText());
            setGraphic(imageView);
            imageView.imageProperty().bind(
                    Bindings.when(
                            item.readProperty()
                    ).then(readImage)
                            .otherwise(unreadImage)
            );
            styleProperty().bind(
                    Bindings.when(
                            item.readProperty()
                    ).then("-fx-background-color: red;")
                            .otherwise("-fx-background-color: null;")
            );
        }
    }
};

从上面处理此问题的另一种方法(也可能是更好的方法)是获取单元格的样式类或psuedoclass,并根据布尔属性更新它。然后在单独的CSS样式表中定义样式。以下示例的输出与上面基于图形的示例相同。

mail.css

.readable:read {
    -fx-background-color: red;
    -fx-graphic: url(
        "http://icons.iconarchive.com/icons/icons8/ios7/16/Messaging-Read-Message-icon.png"
    );
}

.readable:unread {
    -fx-graphic: url(
        "http://icons.iconarchive.com/icons/oxygen-icons.org/oxygen/16/Status-mail-unread-new-icon.png"
    );
}

基于伪类的代码段:

PseudoClass READ_PSEUDO_CLASS = PseudoClass.getPseudoClass("read");
PseudoClass UNREAD_PSEUDO_CLASS = PseudoClass.getPseudoClass("unread");
tree.setCellFactory(new Callback<TreeView<Message>, TreeCell<Message>>() {
    @Override
    public TreeCell<Message> call(TreeView<Message> param) {
        return new TreeCell<Message>() {
            private ChangeListener<Boolean> readChangeListener = (observable, oldValue, newValue) -> {
                pseudoClassStateChanged(READ_PSEUDO_CLASS, newValue);
                pseudoClassStateChanged(UNREAD_PSEUDO_CLASS, !newValue);
            };

            Message priorItem = null;

            {
                getStyleClass().add("readable");
            }

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

                if (priorItem != null) {
                    priorItem.readProperty().removeListener(readChangeListener);
                }

                priorItem = item;

                if (empty || item == null || item.getText() == null) {
                    setText(null);
                    pseudoClassStateChanged(READ_PSEUDO_CLASS, false);
                    pseudoClassStateChanged(UNREAD_PSEUDO_CLASS, false);
                } else {
                    item.readProperty().addListener(readChangeListener);
                    setText(item.getText());
                    pseudoClassStateChanged(READ_PSEUDO_CLASS, item.isRead());
                    pseudoClassStateChanged(UNREAD_PSEUDO_CLASS, !item.isRead());
                }
            }
        };
    }
});