在javaFX的特定行上应用CSS

Wil*_*ina 2 javafx

我在项目中使用TreeTableView,并且当用户选择一行时,我想做一些特定的事情:

我希望该行具有不同的背景色,但我也希望它的孩子和父母也具有这种颜色。

我找到了一种访问所有行和子级的方法,但是我只是不知道如何指定这种背景色。我尝试使用海关TreeTableCells并在updateItem方法中添加样式来执行此操作,但是每次选择一个项目时都不会调用此方法。

所以我想尝试在treetableview中添加侦听器,这似乎是一个更好的主意,但实际上我无法访问行以提供任何样式。

Jam*_*s_D 5

这里的基本策略是:

  1. 为要突出显示的条件创建CSS PseudoClass实例(在下面的示例中,我为“所选子级”创建了一个实例,为“所选父级”创建了一个实例)
  2. 使用rowFactory为表格创建行。这些行应在updateItem方法中更新其伪类状态,并且pseudoclass如果所选项目发生更改,则还应更新其状态。您可以在表的所选项目上放置一个侦听器,以执行其中的第二项。
  3. 使用第一步中定义的伪类,在外部CSS文件中添加CSS,以按所需的方式对行进行样式设置。

这是一个SSCCE:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Function;

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener.Change;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableRow;
import javafx.scene.control.TreeTableView;
import javafx.stage.Stage;

public class TreeTableViewHighlightSelectedPath extends Application {

    private PseudoClass childOfSelected = PseudoClass.getPseudoClass("child-of-selected");
    private PseudoClass parentOfSelected = PseudoClass.getPseudoClass("parent-of-selected");

    @Override
    public void start(Stage primaryStage) {
        TreeTableView<Item> table = new TreeTableView<>(createRandomTree(50));

        table.setRowFactory(ttv -> {
            TreeTableRow<Item> row = new TreeTableRow<Item>() {
                @Override
                protected void updateItem(Item item, boolean empty) {
                    super.updateItem(item, empty);
                    if (empty) {
                        pseudoClassStateChanged(parentOfSelected, false);
                        pseudoClassStateChanged(childOfSelected, false);
                    } else {
                        updateState(this);
                    }
                }
            };

            table.getSelectionModel().getSelectedItems().addListener(
                    (Change<? extends TreeItem<Item>> c) -> updateState(row));

            return row ;
        });

        table.getColumns().add(column("Item", Item::nameProperty));
        table.getColumns().add(column("Value", Item::valueProperty));

        Scene scene = new Scene(table, 800, 800);
        scene.getStylesheets().add("table-row-highlight.css");
        primaryStage.setScene(scene);
        primaryStage.show();

    }

    private <T> void updateState(TreeTableRow<T> row) {
        TreeTableView<T> table = row.getTreeTableView() ;
        TreeItem<T> item = row.getTreeItem();

        // if item is selected, just use default "selected" highlight,
        // and set "child-of-selected" and "parent-of-selected" to false:
        if (item == null || table.getSelectionModel().getSelectedItems().contains(item)) {
            row.pseudoClassStateChanged(childOfSelected, false);
            row.pseudoClassStateChanged(parentOfSelected, false);
            return ;
        }

        // check to see if item is parent of any selected item:
        for (TreeItem<T> selectedItem : table.getSelectionModel().getSelectedItems()) {
            for (TreeItem<T> parent = selectedItem.getParent(); parent != null ; parent = parent.getParent()) {
                if (parent == item) {
                    row.pseudoClassStateChanged(parentOfSelected, true);
                    row.pseudoClassStateChanged(childOfSelected, false);
                    return ;
                }
            }
        }

        // check to see if item is child of any selected item:
        for (TreeItem<T> ancestor = item.getParent() ; ancestor != null ; ancestor = ancestor.getParent()) {
            if (table.getSelectionModel().getSelectedItems().contains(ancestor)) {
                row.pseudoClassStateChanged(childOfSelected, true);
                row.pseudoClassStateChanged(parentOfSelected, false);
                return ;
            }
        }

        // if we got this far, clear both pseudoclasses:

        row.pseudoClassStateChanged(childOfSelected, false);
        row.pseudoClassStateChanged(parentOfSelected, false);

    }

    private <S,T> TreeTableColumn<S,T> column(String title, Function<S, ObservableValue<T>> property) {
        TreeTableColumn<S,T> column = new TreeTableColumn<>(title);
        column.setCellValueFactory(cellData -> property.apply(cellData.getValue().getValue()));
        return column ;
    }

    private TreeItem<Item> createRandomTree(int numNodes) {
        Random rng = new Random();
        TreeItem<Item> root = new TreeItem<>(new Item("Item 1", rng.nextInt(1000)));
        root.setExpanded(true);
        List<TreeItem<Item>> items = new ArrayList<>();
        items.add(root);

        for (int i = 2 ; i <= numNodes; i++) {
            Item item = new Item("Item "+i, rng.nextInt(1000));
            TreeItem<Item> treeItem = new TreeItem<>(item);
            treeItem.setExpanded(true);
            items.get(rng.nextInt(items.size())).getChildren().add(treeItem);
            items.add(treeItem);
        }

        return root ;
    }

    public static class Item {
        private StringProperty name = new SimpleStringProperty();
        private IntegerProperty value = new SimpleIntegerProperty();

        public Item(String name, int value) {
            setName(name);
            setValue(value);
        }

        public final StringProperty nameProperty() {
            return this.name;
        }


        public final java.lang.String getName() {
            return this.nameProperty().get();
        }


        public final void setName(final java.lang.String name) {
            this.nameProperty().set(name);
        }


        public final IntegerProperty valueProperty() {
            return this.value;
        }


        public final int getValue() {
            return this.valueProperty().get();
        }


        public final void setValue(final int value) {
            this.valueProperty().set(value);
        }

        @Override
        public String toString() {
            return String.format("%s (%d)", getName(), getValue());
        }

    }

    public static void main(String[] args) {
        launch(args);
    }
}
Run Code Online (Sandbox Code Playgroud)

和CSS文件(table-row-highlight.css):

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Function;

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener.Change;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableRow;
import javafx.scene.control.TreeTableView;
import javafx.stage.Stage;

public class TreeTableViewHighlightSelectedPath extends Application {

    private PseudoClass childOfSelected = PseudoClass.getPseudoClass("child-of-selected");
    private PseudoClass parentOfSelected = PseudoClass.getPseudoClass("parent-of-selected");

    @Override
    public void start(Stage primaryStage) {
        TreeTableView<Item> table = new TreeTableView<>(createRandomTree(50));

        table.setRowFactory(ttv -> {
            TreeTableRow<Item> row = new TreeTableRow<Item>() {
                @Override
                protected void updateItem(Item item, boolean empty) {
                    super.updateItem(item, empty);
                    if (empty) {
                        pseudoClassStateChanged(parentOfSelected, false);
                        pseudoClassStateChanged(childOfSelected, false);
                    } else {
                        updateState(this);
                    }
                }
            };

            table.getSelectionModel().getSelectedItems().addListener(
                    (Change<? extends TreeItem<Item>> c) -> updateState(row));

            return row ;
        });

        table.getColumns().add(column("Item", Item::nameProperty));
        table.getColumns().add(column("Value", Item::valueProperty));

        Scene scene = new Scene(table, 800, 800);
        scene.getStylesheets().add("table-row-highlight.css");
        primaryStage.setScene(scene);
        primaryStage.show();

    }

    private <T> void updateState(TreeTableRow<T> row) {
        TreeTableView<T> table = row.getTreeTableView() ;
        TreeItem<T> item = row.getTreeItem();

        // if item is selected, just use default "selected" highlight,
        // and set "child-of-selected" and "parent-of-selected" to false:
        if (item == null || table.getSelectionModel().getSelectedItems().contains(item)) {
            row.pseudoClassStateChanged(childOfSelected, false);
            row.pseudoClassStateChanged(parentOfSelected, false);
            return ;
        }

        // check to see if item is parent of any selected item:
        for (TreeItem<T> selectedItem : table.getSelectionModel().getSelectedItems()) {
            for (TreeItem<T> parent = selectedItem.getParent(); parent != null ; parent = parent.getParent()) {
                if (parent == item) {
                    row.pseudoClassStateChanged(parentOfSelected, true);
                    row.pseudoClassStateChanged(childOfSelected, false);
                    return ;
                }
            }
        }

        // check to see if item is child of any selected item:
        for (TreeItem<T> ancestor = item.getParent() ; ancestor != null ; ancestor = ancestor.getParent()) {
            if (table.getSelectionModel().getSelectedItems().contains(ancestor)) {
                row.pseudoClassStateChanged(childOfSelected, true);
                row.pseudoClassStateChanged(parentOfSelected, false);
                return ;
            }
        }

        // if we got this far, clear both pseudoclasses:

        row.pseudoClassStateChanged(childOfSelected, false);
        row.pseudoClassStateChanged(parentOfSelected, false);

    }

    private <S,T> TreeTableColumn<S,T> column(String title, Function<S, ObservableValue<T>> property) {
        TreeTableColumn<S,T> column = new TreeTableColumn<>(title);
        column.setCellValueFactory(cellData -> property.apply(cellData.getValue().getValue()));
        return column ;
    }

    private TreeItem<Item> createRandomTree(int numNodes) {
        Random rng = new Random();
        TreeItem<Item> root = new TreeItem<>(new Item("Item 1", rng.nextInt(1000)));
        root.setExpanded(true);
        List<TreeItem<Item>> items = new ArrayList<>();
        items.add(root);

        for (int i = 2 ; i <= numNodes; i++) {
            Item item = new Item("Item "+i, rng.nextInt(1000));
            TreeItem<Item> treeItem = new TreeItem<>(item);
            treeItem.setExpanded(true);
            items.get(rng.nextInt(items.size())).getChildren().add(treeItem);
            items.add(treeItem);
        }

        return root ;
    }

    public static class Item {
        private StringProperty name = new SimpleStringProperty();
        private IntegerProperty value = new SimpleIntegerProperty();

        public Item(String name, int value) {
            setName(name);
            setValue(value);
        }

        public final StringProperty nameProperty() {
            return this.name;
        }


        public final java.lang.String getName() {
            return this.nameProperty().get();
        }


        public final void setName(final java.lang.String name) {
            this.nameProperty().set(name);
        }


        public final IntegerProperty valueProperty() {
            return this.value;
        }


        public final int getValue() {
            return this.valueProperty().get();
        }


        public final void setValue(final int value) {
            this.valueProperty().set(value);
        }

        @Override
        public String toString() {
            return String.format("%s (%d)", getName(), getValue());
        }

    }

    public static void main(String[] args) {
        launch(args);
    }
}
Run Code Online (Sandbox Code Playgroud)

这给出了以下内容:

在此处输入图片说明

此版本突出显示树中所选项目的所有后代节点和所有祖先节点。updateState()如果仅希望突出显示直接的子行和父行,则可以简化该方法。