JavaFX Border不适用于具有自定义形状的节点

时间:2018-10-29 15:57:42

标签: java javafx

我正在尝试找出是否可以为具有自定义形状的节点绘制边框。当前边框不适合节点的形状。

这是当前的样子:
enter image description here

形状是通过以下CSS实现的:

.arrow-tail {
    -fx-shape: "M 0 0 L 10 0 L 10 10 L 0 10 L 10 5 Z";
}

.arrow-head {
    -fx-shape: "M 0 0 L 10 5 L 0 10 Z";
}

这是使用CSS的arrow类的重要代码:

public class Arrow extends HBox {

    public void Arrow(Node graphic, String title) {
        getChildren().addAll(getArrowTail(), getArrowMiddlePart(graphic, title), getArrowHead());
    }

    private final Region getArrowTail() {
        final Region arrowTail = new Region();
        arrowTail.setMinWidth(10);
        arrowTail.getStyleClass().add("arrow-tail");
        return arrowTail;
     }

     private final Node getArrowMiddlePart(Node graphic, String text) {
        labelTitle = new Label(text);
        labelTitle.setGraphic(graphic);
        labelTitle.idProperty().bind(idProperty());

        final Tooltip tooltip = new Tooltip();
        tooltip.textProperty().bind(labelTitle.textProperty());
        Tooltip.install(labelTitle, tooltip);

        final HBox arrowMiddlePart = new HBox(labelTitle);
        arrowMiddlePart.minWidthProperty().bind(minWidthProperty());
        arrowMiddlePart.setAlignment(Pos.CENTER);
        return arrowMiddlePart;
   }

    private final Region getArrowHead() {
        final Region arrowHead = new Region();
        arrowHead.setMinWidth(10);
        arrowHead.getStyleClass().add("arrow-head");
        return arrowHead;
    }  

}

Arrow类是一个HBox,我在其中创建一个自定义形状的区域,如箭头的尾巴和箭头,另一个HBox中带有标签的区域作为箭头的中间部分。

1 个答案:

答案 0 :(得分:1)

不幸的是,似乎没有一种方法可以为Region的不同边设置边界,并单独应用形状。

我建议扩展Region作为第一个孩子直接添加Path并覆盖layoutChildren来调整此路径的大小。

public class Arrow extends Region {

    private static final double ARROW_LENGTH = 10;
    private static final Insets MARGIN = new Insets(1, ARROW_LENGTH, 1, ARROW_LENGTH);

    private final HBox container;
    private final HLineTo hLineTop;
    private final LineTo tipTop;
    private final LineTo tipBottom;
    private final LineTo tailBottom;

    public Arrow(Node graphic, String title) {
        Path path = new Path(
                new MoveTo(),
                hLineTop = new HLineTo(),
                tipTop = new LineTo(ARROW_LENGTH, 0),
                tipBottom = new LineTo(-ARROW_LENGTH, 0),
                new HLineTo(),
                tailBottom = new LineTo(ARROW_LENGTH, 0),
                new ClosePath());

        tipTop.setAbsolute(false);
        tipBottom.setAbsolute(false);

        path.setManaged(false);
        path.setStrokeType(StrokeType.INSIDE);
        path.getStyleClass().add("arrow-shape");

        Label labelTitle = new Label(title, graphic);
        container = new HBox(labelTitle);

        getChildren().addAll(path, container);
        HBox.setHgrow(labelTitle, Priority.ALWAYS);
        labelTitle.setAlignment(Pos.CENTER);
        labelTitle.setMaxWidth(Double.POSITIVE_INFINITY);
    }

    @Override
    protected void layoutChildren() {
        // hbox layout
        Insets insets = getInsets();
        double left = insets.getLeft();
        double top = insets.getTop();
        double width = getWidth();
        double height = getHeight();
        layoutInArea(container,
                left, top,
                width - left - insets.getRight(), height - top - insets.getBottom(),
                0, MARGIN, true, true, HPos.LEFT, VPos.TOP);

        // adjust arrow shape
        double length = width - ARROW_LENGTH;
        double h2 = height / 2;

        hLineTop.setX(length);

        tipTop.setY(h2);
        tipBottom.setY(h2);
        tailBottom.setY(h2);
    }

    @Override
    protected double computeMinWidth(double height) {
        Insets insets = getInsets();
        return 2 * ARROW_LENGTH + insets.getLeft() + insets.getRight() + container.minWidth(height);
    }

    @Override
    protected double computeMinHeight(double width) {
        Insets insets = getInsets();
        return 2 + insets.getTop() + insets.getBottom() + container.minHeight(width);
    }

    @Override
    protected double computePrefWidth(double height) {
        Insets insets = getInsets();
        return 2 * ARROW_LENGTH + insets.getLeft() + insets.getRight() + container.prefWidth(height);
    }

    @Override
    protected double computePrefHeight(double width) {
        Insets insets = getInsets();
        return 2 + insets.getTop() + insets.getBottom() + container.prefHeight(width);
    }

    @Override
    protected double computeMaxWidth(double height) {
        Insets insets = getInsets();
        return 2 * ARROW_LENGTH + insets.getLeft() + insets.getRight() + container.maxWidth(height);
    }

    @Override
    protected double computeMaxHeight(double width) {
        Insets insets = getInsets();
        return 2 + insets.getTop() + insets.getBottom() + container.maxHeight(width);
    }

}

CSS

.arrow-shape {
    -fx-fill: dodgerblue;
    -fx-stroke: black;
}

请注意,如果扩展HBox,代码将更简单,但这将允许其他类访问子列表,这可能会导致删除Path;扩展Region允许我们保留方法protected来防止这种访问,但是需要我们实现compute...方法和子项的布局。