我有一个如下所示的 node :

enter image description here

只使用css时,我希望标签覆盖其父边框 colored颜色 ,这样标签下的边框 colored颜色 部分将不可见.

我用来制作这个边框的css代码:

-fx-border-color: black;
-fx-border-width: 3;
-fx-border-radius: 8;

Additional info:

1 ->可以通过在标签上放置背景 colored颜色 :白色来达到同样的效果,尽管在我的情况下这不可能.

2 ->出于性能/ arch 的原因,不可能通过推荐中建议的带有样式的BorderPane或TitledPane node 来实现此效果.因此,只使用css或至少使用较少的Java代码非常重要.

推荐答案

@珠海方法+1. 或多或少,我也确实有一个类似的方法来使用路径(包括拐角半径)动态构建边界.我还有另外两种方法,一种是使用inverse clipping技巧,另一种是使用border segments技巧.

我在下面的演示中包含了所有这三种方法.您可以 Select 或忽略.但我的主要意图是提供一个方向,如果有人对此感兴趣的话.

enter image description here

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import javafx.scene.shape.*;
import javafx.stage.Stage;

public class TitledBorderDemo extends Application {
    String sampleText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";

    @Override
    public void start(final Stage stage) throws Exception {
        VBox root = new VBox(20);
        root.setStyle("-fx-background-color:linear-gradient(to bottom, pink, yellow);");
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(15));

        buildWithPath(root);
        buildWithClip(root);
        buildWithStyle(root);

        Scene scene = new Scene(root, 600, 600);
        scene.getStylesheets().add(getClass().getResource("titledborder.css").toExternalForm());
        stage.setScene(scene);
        stage.setTitle("Titled Border Demo");
        stage.show();
    }

    private void buildWithPath(VBox root) {
        Label content = new Label();
        content.setWrapText(true);
        content.setText(sampleText);

        TitledBorderWithPath pane = new TitledBorderWithPath();
        pane.setTitle("With Path Approach");
        pane.setContent(content);
        root.getChildren().add(pane);
    }

    private void buildWithClip(VBox root) {
        Label content = new Label();
        content.setWrapText(true);
        content.setText(sampleText);

        TitledBorderWithClip pane = new TitledBorderWithClip();
        pane.setTitle("With Clip Approach");
        pane.setContent(content);
        root.getChildren().add(pane);
    }

    private void buildWithStyle(VBox root) {
        Label content = new Label();
        content.setWrapText(true);
        content.setText(sampleText);

        TitledBorderWithSegment pane = new TitledBorderWithSegment();
        pane.setTitle("With Segment Approach");
        pane.setContent(content);
        root.getChildren().add(pane);
    }

    /**
     * Approach by using border segment.
     */
    class TitledBorderWithSegment extends StackPane {
        private final Label titleLabel = new Label();

        public TitledBorderWithSegment() {
            getStyleClass().add("titled-border-segment");
            titleLabel.getStyleClass().add("title-label");
            getChildren().addAll(titleLabel);
            // Position the title label on the top border
            titleLabel.translateYProperty().bind(titleLabel.heightProperty().divide(2).add(titleLabel.layoutYProperty()).multiply(-1));

            titleLabel.needsLayoutProperty().addListener((obs, old, needsLayout) -> {
                if (!needsLayout) {
                    buildStyle();
                }
            });

            widthProperty().addListener(p -> buildStyle());
            heightProperty().addListener(p -> buildStyle());
        }

        private void buildStyle() {
            double buffer = 8;
            double r = getBorder().getStrokes().get(0).getRadii().getTopLeftHorizontalRadius();
            double arcLength = Math.round(Math.toRadians(90) * r);
            double w = getWidth();
            double h = getHeight();
            double titleX = titleLabel.getLayoutX() + titleLabel.getTranslateX();
            double titleLength = titleLabel.getWidth();
            double t = (h - (2 * r)) +
                    arcLength +
                    (w - (2 * r)) +
                    arcLength +
                    (h - (2 * r)) +
                    arcLength +
                    (w - titleX - titleLength - r) - buffer;

            setStyle("-fx-border-style: segments(" + t + ", " + titleLength + ") line-cap round;");
        }

        public void setTitle(final String title) {
            this.titleLabel.setText(title);
        }

        public void setContent(Node node) {
            getChildren().clear();
            getChildren().addAll(titleLabel, node);
        }
    }

    /**
     * Approach by using Path.
     */
    class TitledBorderWithPath extends StackPane {
        private final Path border = new Path();
        private final StackPane container = new StackPane();
        private final Label titleLabel = new Label();

        // Can configure this as a CSS styleable property.
        private final double borderRadius = 8;

        public TitledBorderWithPath() {
            getStyleClass().add("titled-border-path");
            getChildren().addAll(border, container);

            border.getStyleClass().add("border");
            border.setManaged(false);

            titleLabel.getStyleClass().add("title-label");
            // Position the title label on the top border
            titleLabel.translateYProperty().bind(titleLabel.heightProperty().divide(2).add(titleLabel.layoutYProperty()).multiply(-1));
            titleLabel.needsLayoutProperty().addListener((obs, old, needsLayout) -> {
                if (!needsLayout) {
                    drawBorder();
                }
            });

            container.getStyleClass().add("container");
            container.widthProperty().addListener(p -> drawBorder());
            container.heightProperty().addListener(p -> drawBorder());
        }

        private void drawBorder() {
            double w = container.getWidth();
            double h = container.getHeight();
            double r = borderRadius;
            double x = titleLabel.getLayoutX() + titleLabel.getTranslateX();

            border.getElements().clear();
            border.getElements().addAll(new MoveTo(x, 0),
                    new LineTo(r, 0),
                    arc(0, r), // Top left
                    new LineTo(0, h - r),
                    arc(r, h),  // Bottom left
                    new LineTo(w - r, h),
                    arc(w, h - r), // Bottom right
                    new LineTo(w, r),
                    arc(w - r, 0), // Top right
                    new LineTo(x + titleLabel.getWidth(), 0));
        }

        private ArcTo arc(double x, double y) {
            return new ArcTo(borderRadius, borderRadius, 0, x, y, false, false);
        }

        public void setTitle(final String title) {
            this.titleLabel.setText(title);
        }

        public void setContent(Node node) {
            container.getChildren().clear();
            container.getChildren().addAll(titleLabel, node);
        }
    }

    /**
     * Approach by using clipping.
     */
    class TitledBorderWithClip extends StackPane {
        private Label titleLabel;

        final Rectangle clip = new Rectangle();
        final Rectangle inverse = new Rectangle();

        final Pane border = new Pane();
        final StackPane container = new StackPane();

        public TitledBorderWithClip() {
            getStyleClass().add("titled-border-clip");
            titleLabel = new Label();
            titleLabel.getStyleClass().add("title-label");

            container.getChildren().add(titleLabel);
            container.getStyleClass().add("container");

            border.getStyleClass().add("border");

            getChildren().addAll(border, container);


            border.widthProperty().addListener(p -> setInverseClip(border, clip));
            border.heightProperty().addListener(p -> setInverseClip(border, clip));

            // Position the title label on the top border
            titleLabel.translateYProperty().bind(titleLabel.heightProperty().divide(2).add(titleLabel.layoutYProperty()).multiply(-1));
            titleLabel.needsLayoutProperty().addListener((obs, old, needsLayout) -> {
                if (!needsLayout) {
                    setInverseClip(border, clip);
                }
            });
        }

        public void setTitle(final String title) {
            this.titleLabel.setText(title);
        }

        public void setContent(Node node) {
            container.getChildren().clear();
            container.getChildren().addAll(titleLabel, node);
        }

        private void setInverseClip(final Pane node, final Rectangle clip) {
            clip.setWidth(titleLabel.getWidth());
            clip.setHeight(titleLabel.getHeight());
            clip.setX(titleLabel.getLayoutX() + titleLabel.getTranslateX());
            clip.setY(titleLabel.getLayoutY() + titleLabel.getTranslateY());

            inverse.setWidth(node.getWidth());
            inverse.setHeight(node.getHeight());
            node.setClip(Shape.subtract(inverse, clip));
        }
    }
}

titledborder.css

.title-label{
    -fx-padding: 0px 5px 0px 5px;
    -fx-font-weight: bold;
    -fx-font-size: 14px;
    -fx-translate-x: 5px; /* To adjust the title placement horizontally */
}

/* With Clip Approach */
.titled-border-clip {
    -fx-alignment: TOP_LEFT;
}

.titled-border-clip > .container{
    -fx-padding:10px 5px 5px 5px;
    -fx-alignment: TOP_LEFT;
}

.titled-border-clip > .border{
    -fx-border-color:red;
    -fx-border-width: 2px;
    -fx-border-radius: 5px;
}

/* With Path Approach */
.titled-border-path {
    -fx-alignment: TOP_LEFT;
}

.titled-border-path > .container{
    -fx-padding:10px 5px 5px 5px;
    -fx-alignment: TOP_LEFT;
}

.titled-border-path > .border{
    -fx-stroke:red;
    -fx-stroke-width:2px;
}

/* With Simple Approach */
.titled-border-simple{
    -fx-background-color:inherit;
    -fx-border-color:red;
    -fx-border-width: 2px;
    -fx-border-radius: 5px;
    -fx-padding:10px 5px 5px 5px;
    -fx-alignment: TOP_LEFT;
}

.titled-border-simple > .title-label{
    -fx-background-color:inherit;
    -fx-border-color:inherit;
    -fx-border-width:inherit;
    -fx-border-radius:inherit;
}

/* With Segment Approach */
.titled-border-segment{
    -fx-border-color:red;
    -fx-border-width: 2px;
    -fx-border-radius: 10px;
    -fx-padding:10px 5px 5px 5px;
    -fx-alignment: TOP_LEFT;
}

For Simple layouts

好吧,对于doesn't具有动态背景(如渐变、图像)的布局来说,上述方法可能有些矫枉过正.等).对于简单的布局,就像@Slaw在第一条 comments 中提到的那样,在父级和标签上都设置-fx-background-color: inherit;应该可以做到这一点.

enter image description here

class SimpleTitledBorder extends StackPane {
        private final Label titleLabel = new Label();

        public SimpleTitledBorder() {
            getStyleClass().add("titled-border-simple");
            titleLabel.getStyleClass().add("title-label");
            getChildren().addAll(titleLabel);
            // Position the title label on the top border
            titleLabel.translateYProperty().bind(titleLabel.heightProperty().divide(2).add(titleLabel.layoutYProperty()).multiply(-1));
        }
        public void setTitle(final String title) {
            this.titleLabel.setText(title);
        }

        public void setContent(Node node) {
            getChildren().clear();
            getChildren().addAll(titleLabel, node);
        }
    }

Css代码:

/* With Simple Approach */
.title-label{
    -fx-padding: 0px 5px 0px 5px;
    -fx-font-weight: bold;
    -fx-font-size: 14px;
    -fx-translate-x: 5px; /* To adjust the title placement horizontally */
}
.titled-border-simple{
    -fx-background-color:inherit;
    -fx-border-color:red;
    -fx-border-width: 2px;
    -fx-border-radius: 5px;
    -fx-padding:10px 5px 5px 5px;
    -fx-alignment: TOP_LEFT;
}

.titled-border-simple > .title-label{
    -fx-background-color:inherit;
}

并继承要标记的边框属性,为布局提供了另一种外观:)

enter image description here

.titled-border-simple > .title-label{
    -fx-background-color:inherit;
    -fx-border-color:inherit;
    -fx-border-width:inherit;
    -fx-border-radius:inherit;
}

Java相关问答推荐

Java中Stream(java.util.stream)和linkedList数据 struct 之间是什么关系?

无法从TemporalAccessor获取Instant:{},ISO解析为2024-04- 25 T14:32:42类型为java.time. form.Parsed

那么比较似乎不是词典学的,尽管doctor 这么说

基于仅存在于父级中的字段查询子文档?

如何审查Java dtos中的自定义注释字段?

有关手动创建的包的问题

无法处理批处理侦听器中的反序列化异常

Java 21虚拟线程执行器的性能比池化操作系统线程的执行器差?

在Java Swing Paint应用程序中捕获快速鼠标移动时遇到困难

Java中实现的归并排序算法给出ArrayIndexOutOfBound异常

使用传递的参数构造异常的Mockito-doThrow(或thenThrow)

SpringBoot Kafka自动配置-适用于SASL_PLAYTEXT的SSLBundle 包,带SCRAM-SHA-512

Kotlin Val是否提供了与Java最终版相同的可见性保证?

Java泛型类方法的静态返回类型是否被类型擦除?

buildDir:File!&#的getter解决方案是什么?39.被抛弃

Java.lang.invke.LambdaConversionException:实例方法InvokeVirtual的参数数量不正确

如何获得凌空cookies ,并设置它在下一个请求- android

不能在 map 上移除折线

基于距离的APACHE POI公式判断

java21预览未命名的符号用于try-with-resources