【问题标题】:JavaFx TreeView cssJavaFx 树视图 css
【发布时间】:2015-08-21 10:52:59
【问题描述】:

我想创建如下所示的外观和行为元素。

有一个深色背景和 4 个元素的顶部列表:“biblioteki”、“Analiza”等。当我们单击其中一个时,列表会展开,并且该项目及其子项将变为浅暗背景。此外,子列表中的选定项目将获得不同的字体(粗体和白色)。任何时候都只能展开一项。

所以我发现这是应用了适当样式的 TreeView 行为。 我可以使用以下代码:

    TreeView<TabMenuElement> treeView = new TreeView<>(treeRoot);
    treeView.setCellFactory(tv -> new TreeCell<TabMenuElement>() {
        @Override
        public void updateItem(TabMenuElement item, boolean empty) {
            super.updateItem(item, empty);
            setDisclosureNode(null);

            if (empty) {
                setText("");
                setGraphic(null);
            } else {
                setText(item.getName()); // appropriate text for item
                if (item.getIv() != null) {
                    setGraphic(item.getIv());
                }
            }
        }
    });
    treeView.setShowRoot(false);

TabMenuElement 具有方法 getIV 来获取 ImageView(图标)(如果它是为此元素定义的)和 getName 来获取要显示的文本。目前看起来是这样的

所以我有以下问题:

  1. 如何仅更改 TreeView 中选定项目的字体?
  2. 如何在选定的子树上设置背景?
  3. 如何强制最多可以展开一个子树
  4. 如何为顶级项目设置更大的尺寸?

【问题讨论】:

    标签: javafx treeview


    【解决方案1】:

    如何仅更改 TreeView 中选定项目的字体?

    在您的 css 文件中,只需定义 .tree-cell:selected 的字体:

    .tree-cell:selected {
        -fx-font-weight: bold ;
    }
    

    如何在选定的子树上设置背景?

    这个有点棘手。您希望任何扩展节点的背景以及其父节点不是根节点的任何节点都不同(这不是您的措辞,但我认为它在逻辑上是等效的)。展开的节点已经有一个 CSS 伪类。对于“父不是根”,需要自己定义伪类:

    PseudoClass subElementPseudoClass = PseudoClass.getPseudoClass("sub-tree-item");
    

    现在观察单元格的treeItem 属性,并在它发生变化时更新伪类状态:

    treeView.setCellFactory(tv -> {
        TreeCell<TabMenuElement> cell = new TreeCell<TabMenuElement>() {
            @Override
            public void updateItem(TabMenuElement item, boolean empty) {
                super.updateItem(item, empty);
                setDisclosureNode(null);
    
                if (empty) {
                    setText("");
                    setGraphic(null);
                } else {
                    setText(item.getName()); // appropriate text for item
                    if (item.getIv() != null) {
                        setGraphic(item.getIv());
                    }
                }
            }
        };
    
        cell.treeItemProperty().addListener((obs, oldTreeItem, newTreeItem) -> {
            cell.pseudoClassStateChanged(subElementPseudoClass,
                newTreeItem != null && newTreeItem.getParent() != cell.getTreeView().getRoot());
        }
    
        return cell ;
    });
    

    现在你可以在你的 CSS 文件中做

    .tree-cell:expanded, .tree-cell:sub-tree-item {
        -fx-background-color: ... ;
    }
    

    如何强制最多可以展开一个子树

    将以下ChangeListener 添加到TreeItem 的每个扩展属性中:

    ChangeListener<Boolean> expandedListener = (obs, wasExpanded, isNowExpanded) -> {
        if (isNowExpanded) {
            ReadOnlyProperty<?> expandedProperty = (ReadOnlyProperty<?>) obs ;
            Object itemThatWasJustExpanded = expandedProperty.getBean();
            for (TreeItem<TabMenuElement> item : treeView.getRoot().getChildren()) {
                if (item != itemThatWasJustExpanded) {
                    item.setExpanded(false);
                }
            }
        }
    };
    
    TreeItem<TabMenuElement> biblioteka = new TreeItem<>(...);
    biblioteka.expandedProperty().addListener(expandedListener);
    TreeItem<TabMenuElement> analiza = new TreeItem<>(...);
    analiza.expandedProperty().addListener(expandedListener);
    // etc, for all "top-level" items.
    

    如何为顶级项目设置更大的尺寸?

    .tree-cell {
        -fx-padding: 0.75em 0em 0.75em 0em ;
    }
    .tree-cell:sub-tree-item {
        -fx-padding: 0.25em ;
    }
    

    (或更改字体大小,或类似的。)

    这是一个完整的例子:

    import javafx.application.Application;
    import javafx.beans.property.ReadOnlyProperty;
    import javafx.beans.value.ChangeListener;
    import javafx.css.PseudoClass;
    import javafx.scene.Scene;
    import javafx.scene.control.TreeCell;
    import javafx.scene.control.TreeItem;
    import javafx.scene.control.TreeView;
    import javafx.scene.layout.BorderPane;
    import javafx.stage.Stage;
    
    public class StyledUniqueExpandingTree extends Application {
    
        @Override
        public void start(Stage primaryStage) {
            TreeView<String> tree = new TreeView<>();
            tree.setShowRoot(false);
            TreeItem<String> root = new TreeItem<>("");
            tree.setRoot(root);
    
            ChangeListener<Boolean> expandedListener = (obs, wasExpanded, isNowExpanded) -> {
                if (isNowExpanded) {
                    ReadOnlyProperty<?> expandedProperty = (ReadOnlyProperty<?>) obs ;
                    Object itemThatWasJustExpanded = expandedProperty.getBean();
                    for (TreeItem<String> item : tree.getRoot().getChildren()) {
                        if (item != itemThatWasJustExpanded) {
                            item.setExpanded(false);
                        }
                    }
                }
            };
    
            for (int i=1; i<=4; i++) {
                TreeItem<String> item = new TreeItem<>("Top level "+i);
                item.expandedProperty().addListener(expandedListener);
                root.getChildren().add(item);
                for (int j=1; j<=4; j++) {
                    TreeItem<String> subItem = new TreeItem<>("Sub item "+i+":"+j);
                    item.getChildren().add(subItem);
                }
            }
    
            PseudoClass subElementPseudoClass = PseudoClass.getPseudoClass("sub-tree-item");
    
            tree.setCellFactory(tv -> {
                TreeCell<String> cell = new TreeCell<String>() {
                    @Override
                    public void updateItem(String item, boolean empty) {
                        super.updateItem(item, empty);
                        setDisclosureNode(null);
    
                        if (empty) {
                            setText("");
                            setGraphic(null);
                        } else {
                            setText(item); // appropriate text for item
                        }
                    }
    
                };
                cell.treeItemProperty().addListener((obs, oldTreeItem, newTreeItem) -> {
                    cell.pseudoClassStateChanged(subElementPseudoClass,
                            newTreeItem != null && newTreeItem.getParent() != cell.getTreeView().getRoot());
                });
                return cell ;
            });
    
            BorderPane uiRoot = new BorderPane(tree);
            Scene scene = new Scene(uiRoot, 250, 400);
            scene.getStylesheets().add("styled-unique-expanded-tree.css");
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    使用 styled-unique-expanded-tree.css:

    .tree-view, .tree-cell {
        -fx-background-color: black ;
        -fx-text-fill: white ;
    }
    .tree-cell:expanded, .tree-cell:sub-tree-item {
        -fx-background-color: #404040 ;
    }
    .tree-cell:selected {
        -fx-font-weight: bold ;
    }
    .tree-cell {
        -fx-padding: 0.75em 0em 0.75em 0em ;
    }
    .tree-cell:sub-tree-item {
        -fx-padding: 0.25em ;
    }
    

    【讨论】:

    • 这项工作非常好。非常感谢!然而我还有两个问题:1.我希望子树在单击时展开(现在它只能在双击时工作)2.我只有顶级元素的图标,但我希望所有级别的标签都对齐到单行。 (现在子级别向右移动)。请指教。
    • 我测试了你的例子,它导致了奇怪的问题测试注意我刚刚复制了 1:1 你的 *.java 和 *.css 文件。在我的情况下,几乎每次都可以看到问题,如果您的应用程序我必须重试几次,但它并不总是可见。以下是重现的步骤: 1. 启动 App 2. 展开最低子树 3. 垂直增加窗口大小(直到屏幕高度) 4. 垂直减小窗口大小到初始大小 5. 再次垂直增加窗口大小(最多屏幕高度)。 6. 现在尝试几次展开 3toplevel 和再次 4toplevel - 结果是在树视图下方显示更亮的条
    【解决方案2】:

    我自己想出了如何在单击树视图上展开:

        treeView.setCellFactory(tv -> new TreeCell<TabMenuElement>() {
            @Override
            public void updateItem(TabMenuElement item, boolean empty) {
                super.updateItem(item, empty);
                setDisclosureNode(null);
    
                if (empty) {
                    setText("");
                    setGraphic(null);
                } else {
                    setText(item.getName()); // appropriate text for item
                    setOnMouseClicked(event -> {
                        if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 1) {
                            TreeItem<TabMenuElement> ti = treeItemProperty().get();
                            ti.expandedProperty().set(true);
                        }
                        event.consume();
                    });
    
                    if (item.getIv() != null) {
                        setGraphic(item.getIv());
                    } else {
    
                    }
                }
            }
        });
    

    特别是带有 setOnMouseClicked 的部分。

    在这种情况下,也可以通过设置 in 为所有节点设置相同的缩进

    .tree-cell:sub-tree-item{
         -fx-indent: 22;
    }
    

    这适用于我的情况,顶级元素的图标宽度为 22。然后我只有一个子级别,在那里我设置了相同的标识。 如果您只想在整个树中使用相同的缩进而没有 icsons,则将所有 .tree-cell 的 indet 设置为 0 就足够了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-30
      • 2018-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多