如何在树视图中禁用或修改树单元格的填充

2022-09-03 15:53:38

环境:JDK 7u75、Windows 8.1 x64、JavaFX2.2

示例代码:

public class TreeViewSample extends Application {

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

    @Override
    public void start(Stage primaryStage) throws MalformedURLException {
        primaryStage.setTitle("Tree View Sample");

        TreeItem<String> rootItem = new TreeItem<String>("RootNode");

        for (int i = 1; i < 5; i++) {
            TreeItem<String> item = new TreeItem<String>("SubNode" + i);
            rootItem.getChildren().add(item);
            item.getChildren().add(new TreeItem<String>("SubSubNode" + i + "" + i));
        }

        rootItem.getChildren().add(new TreeItem<String>("SecondRootNode"));
        TreeView<String> tree = new TreeView<String>(rootItem);
        StackPane root = new StackPane();
        root.getChildren().add(tree);
        Scene scene = new Scene(root, 300, 250);
        scene.getStylesheets().add((new File("../css/styletest.css").toURI()).toURL().toString());

        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

CSS:

.tree-cell {
    -fx-skin: "com.sun.javafx.scene.control.skin.TreeCellSkin";
    -fx-background-color: -fx-control-inner-background;
    -fx-padding: 0.25em; /* 3 */
    -fx-text-fill: -fx-text-inner-color;
    -fx-indent: 10;
}

.tree-cell .label {
    -fx-padding: 0.0em 0.0em 0.0em 0.25em; /* 0 0 0 3 */
}

.tree-cell .tree-disclosure-node {
    -fx-padding: 4 2 4 8;
    -fx-background-color: transparent;
}

.tree-cell .tree-disclosure-node .arrow {
    -fx-background-color: -fx-mark-color;
    -fx-padding: 0.333333em; /* 4 */
    -fx-shape: "M 0 -4 L 8 0 L 0 4 z";
}

was

我们需要删除所有节点的所有空格和箭头。修改后的 CSS:

.tree-cell {
    -fx-skin: "com.sun.javafx.scene.control.skin.TreeCellSkin";
    -fx-background-color: -fx-control-inner-background;
    -fx-padding: 0px; 
    -fx-text-fill: -fx-text-inner-color;
    -fx-indent: 0px;
}

.tree-cell .label {
    -fx-padding: 0.0em 0.0em 0.0em 0.0em;
}

.tree-cell .tree-disclosure-node {
    -fx-padding: 0px;
    -fx-background-color: transparent;
}

.tree-cell .tree-disclosure-node .arrow {
    -fx-background-color: -fx-mark-color;
    -fx-padding: 0.0em;
}

enter image description here

如您所见,除叶子外,所有节点都没有填充和缩进。

问题 - 如何删除\修改此填充?


答案 1

在这种情况下,JavaFX开发人员将标准长度设置为18,并且没有提供更改它的机会。以下是他们的评论:

 /*
 * This is rather hacky - but it is a quick workaround to resolve the
 * issue that we don't know maximum width of a disclosure node for a given
 * TreeView. If we don't know the maximum width, we have no way to ensure
 * consistent indentation for a given TreeView.
 *
 * To work around this, we create a single WeakHashMap to store a max
 * disclosureNode width per TreeView. We use WeakHashMap to help prevent
 * any memory leaks.
 *
 * RT-19656 identifies a related issue, which is that we may not provide
 * indentation to any TreeItems because we have not yet encountered a cell
 * which has a disclosureNode. Once we scroll and encounter one, indentation
 * happens in a displeasing way.
 */

但是,如果确实有必要,那么我们使用反射覆盖 TreeCellSkin 类和 layoutChildren 方法。但这并不安全:

  final double defaultDisclosureWidth = maxDisclosureWidthMap.containsKey(tree) ?
        maxDisclosureWidthMap.get(tree) : 18;   // RT-19656: default width of default disclosure node 

  final double defaultDisclosureWidth =  0;   

全班:

public class CustomTreeCellSkin<T> extends TreeCellSkin<T> {
    public CustomTreeCellSkin(TreeCell control) {
        super(control);
    }

    private boolean disclosureNodeDirty = true;


    @Override
    protected void layoutChildren(double x, double y, double w, double h) {
        try {

            TreeView<T> tree = getSkinnable().getTreeView();
            if (tree == null) return;

            if (disclosureNodeDirty) {
                Method method =TreeCellSkin.class.getDeclaredMethod("updateDisclosureNode");
                method.setAccessible(true);
                method.invoke(this);
                disclosureNodeDirty=false;
            }

            Node disclosureNode = getSkinnable().getDisclosureNode();

            TreeItem<?> treeItem = getSkinnable().getTreeItem();

            int level = tree.getTreeItemLevel(treeItem);
            if (!tree.isShowRoot()) level--;
            double leftMargin = getIndent() * level;

            x += leftMargin;

            // position the disclosure node so that it is at the proper indent
            boolean disclosureVisible = disclosureNode != null && treeItem != null && !treeItem.isLeaf();
            Field field = TreeCellSkin.class.getDeclaredField("maxDisclosureWidthMap");
            field.setAccessible(true);
            Map<TreeView<?>, Double> maxDisclosureWidthMap = (Map<TreeView<?>, Double>) field.get(this);
            final double defaultDisclosureWidth =  0;   // RT-19656: default width of default disclosure node
            double disclosureWidth = defaultDisclosureWidth;

            if (disclosureVisible) {
                if (disclosureNode == null || disclosureNode.getScene() == null) {
                    updateChildren();
                }
         if (disclosureNode != null) {
                    disclosureWidth = disclosureNode.prefWidth(h);
                    if (disclosureWidth > defaultDisclosureWidth) {
                        maxDisclosureWidthMap.put(tree, disclosureWidth);
                    }

                    double ph = disclosureNode.prefHeight(disclosureWidth);

                    disclosureNode.resize(disclosureWidth, ph);
                    positionInArea(disclosureNode, x, y,
                            disclosureWidth, ph, /*baseline ignored*/0,
                            HPos.CENTER, VPos.CENTER);
                }
            }

            // determine starting point of the graphic or cell node, and the
            // remaining width available to them
            final int padding = treeItem != null && treeItem.getGraphic() == null ? 0 : 3;
            x += disclosureWidth + padding;
            w -= (leftMargin + disclosureWidth + padding);

            // Rather ugly fix for RT-38519, where graphics are disappearing in
            // certain circumstances
            Node graphic = getSkinnable().getGraphic();
            if (graphic != null && !getChildren().contains(graphic)) {
                getChildren().add(graphic);
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        layoutLabelInArea(x, y, w, h);
    }


}

在这种情况下,在 css 中,您需要指定我们重写的类:

.tree-cell {
    -fx-skin: "CustomTreeCellSkin";
    -fx-background-color: -fx-control-inner-background;
    -fx-padding: 0px;
    -fx-text-fill: -fx-text-inner-color;
    -fx-indent: 0px;
}

.tree-cell .label {
    -fx-padding: 0.0em 0.0em 0.0em 0.0em;
}

.tree-cell .tree-disclosure-node {
    -fx-padding: 0px;
    -fx-background-color: transparent;
}

.tree-cell .tree-disclosure-node .arrow {
    -fx-background-color: -fx-mark-color;
    -fx-padding: 0.0em;
}

对不起我的英语。=)


答案 2

虽然我真的很欣赏前面提到的解决方案,但它太复杂了,特别是因为它使用反射,并且必须明确允许JPMS反射使用。

由于回退值是硬编码的,因此我们可以通过填充来否定它。defaultDisclosureWidth

public static class CustomTreeCell extends TreeCell<Foo> {

    static final PseudoClass LEAF = PseudoClass.getPseudoClass("leaf");

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

        // ...
        
        pseudoClassStateChanged(LEAF, getTreeItem().isLeaf());
    }
}
.tree-cell {
    -fx-background-insets: 0;
    -fx-padding: 0;
    -fx-indent:  0;
}

.tree-cell:leaf {
    -fx-padding: 0 0 0 -18px;
}

.tree-cell .tree-disclosure-node,
.tree-cell .arrow {
    -fx-min-width:  0;
    -fx-pref-width: 0;
    -fx-max-width:  0;

    -fx-min-height:  0;
    -fx-pref-height: 0;
    -fx-max-height:  0;
}

使用属性设置所需的任何填充。-fx-indent

简单验证:

TreeItem<Foo> nodeRoot = new TreeItem<>(new Foo("root"));

TreeItem<Foo> nodeTest1 = new TreeItem<>(new Foo("test1"));
nodeRoot.getChildren().add(nodeTest1);

TreeItem<Foo> nodeTest2 = new TreeItem<>(new Foo("test2"));
nodeTest1.getChildren().add(nodeTest2);

TreeItem<Foo> nodeTest3 = new TreeItem<>(new Foo("test3"));
nodeTest2.getChildren().add(nodeTest3);

TreeItem<Foo> nodeTest4 = new TreeItem<>(new Foo("test4"));
nodeTest3.getChildren().add(nodeTest4);

enter image description here


推荐