将样式应用于javaFX中的TreeView子节点

时间:2014-08-14 11:49:44

标签: css javafx treeview

我正在使用JavaFX TreeView,我想实现一个函数,如果我将鼠标悬停在树项上,该项及其所有子项将被突出显示。

到目前为止,我设法使用setCellFactory来突出显示目标项目:

treeCell.setOnMouseEntered(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent mouseEvent) {
        redrawTree()
        treeCell.setStyle("-fx-background-color: #0093ff;");
    }
});

结果是:

enter image description here

但我不知道如何定位和应用样式给treeCell的孩子们。此解决方案还需要重新绘制树,这对于大树来说是滞后的。

任何人都可以帮助我前进或给我另类选择吗?我认为css解决方案会更好。

2 个答案:

答案 0 :(得分:7)

在外部css文件中,执行

.tree-cell:hover {
  -fx-background-color: #0093ff ;
}

另请注意(由于中等复杂的原因)如果您使用-fx-background而不是-fx-background-color,文字颜色会对背景颜色的变化做出适当反应。

要设置子节点的样式(即作为TreeCell属性的一部分添加到graphic的节点),只需执行类似

的操作
.tree-cell:hover .label {
  /* styles... */
}

将在“hovered-over”树状单元格中设置所有标签的样式。

这是一个完整的例子:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TreeTest extends Application {  
    @Override  
    public void start(Stage primaryStage) throws Exception {  
        final StackPane stackPane = new StackPane();  

        TreeItem<Integer> root = createTreeItem(1);

        final TreeView<Integer> tree = new TreeView<>(root);  
        tree.setCellFactory(treeView -> {  
            final Label label = new Label();
            final Label anotherLabel = new Label("Item:");
            label.getStyleClass().add("highlight-on-hover");
            final HBox hbox = new HBox(5, anotherLabel, label);
            TreeCell<Integer> cell =  new TreeCell<Integer>() {  
                @Override  
                protected void updateItem(Integer item, boolean empty) {  
                    super.updateItem(item, empty);  
                    if (empty) {
                        setGraphic(null);
                    } else {
                        setGraphic(hbox);
                    }
                }  
            };  
            cell.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
            cell.itemProperty().addListener((obs, oldItem, newItem) -> 
                label.setText(newItem != null ? String.valueOf(newItem) : ""));
            return cell ;
        });  
        stackPane.getChildren().add(tree);  
        final Scene scene = new Scene(stackPane);  

        scene.getStylesheets().add(getClass().getResource("tree-hover.css").toExternalForm());

        primaryStage.setScene(scene);  
        primaryStage.setTitle(getClass().getSimpleName());  
        primaryStage.show();  
    }  

    private TreeItem<Integer> createTreeItem(int value) {
        TreeItem<Integer> item = new TreeItem<>(value);
        if (value < 10000) {
            for (int i=0; i<10; i++) {
                item.getChildren().add(createTreeItem(10*value+i));
            }
        }
        return item ;
    }

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

使用tree-hover.css文件:

.tree-cell:hover {
    -fx-background-color: #0093ff ;
}

.tree-cell:hover .highlight-on-hover {
    -fx-text-fill: red ;
}

答案 1 :(得分:2)

实现此目的的解决方案是通过将侦听器设置为行工厂来动态添加和删除已定义的样式类。

因此setRowFactory方法负责突出显示子节点。基本上它会将一个监听器附加到onHover属性到每一行。

当触发更改的事件时,我们必须检查该行是否包含已展开且具有子节点的节点。如果是这样,所有孩子将在鼠标悬停时突出显示,并在鼠标移出时突出显示。

private static final String HIGHLIGHT_STYLE = "child-highlight";
private TreeTableView<MyRowContentObject> treeTableView = new TreeTableView<>();
private void setRowFactory() {
    treeTableView.setRowFactory(e -> {
        TreeTableRow<MyRowContentObject> hoverRow = new TreeTableRow<MyRowContentObject>();
        hoverRow.hoverProperty().addListener(new ChangeListener<Boolean>() {
             //you can use a lambda expression for the listener if you want
            @Override
            public void changed(ObservableValue<? extends Boolean> observableValue, Boolean oldStatus, Boolean newStatus) {
                List<Node> rows = getRowsList();
                //on mouse hover
                if(newStatus){
                    TreeItem<MyRowContentObject> item = hoverRow.getTreeItem();
                    //validates if there is a node on the hovered row, is expanded and has children
                    if(item != null && item.isExpanded() && item.getChildren().size() > 0) {
                        //row index of the last child of the expanded and hovered node
                        int lastChildIndex = treeTableView.getRow(item.getChildren().get(item.getChildren().size() - 1));
                        //highlights the childs
                        for (int i = hoverRow.getIndex() + 1; i < lastChildIndex + 1; i++)
                            rows.get(i).getStyleClass().add(HIGHLIGHT_STYLE);
                    }
                } else {
                    //on mouse out cleans every single row, because its hard to keep track of the highlighted rows
                    for (Node row : rows)
                        row.getStyleClass().remove(HIGHLIGHT_STYLE);
                }
            }
        });
        return hoverRow;
    });
}

//fetches all rows from the treeTableView
private List<Node> getRowsList(){
    return new ArrayList<>(treeTableView.lookupAll("TreeTableRow"));
}

并将此添加到styles.css或您定义的任何其他名称:

.child-highlight {
    -fx-background-color: rgba(255, 11, 44, 0.28);
}