JavaFX使节点覆盖父节点边框颜色

2024-05-16

我有一个如下所示的节点:

仅使用 css,我希望标签覆盖其父边框颜色,因此标签下方的边框颜色部分变得不可见。

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

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

附加信息:

1 ->通过将 -background-color:white 放置到标签中可以达到相同的效果,尽管在我的情况下这是不可能的。

2 ->出于性能/体系结构的原因,不可能通过推荐中建议的样式化 BorderPane 或 TitledPane 节点来实现此效果。因此,仅使用 CSS 或至少使用尽可能少的 java 代码非常重要。


@jewelsea 方法+1。 或多或少我也有类似的方法来使用路径(包括角半径)动态构建边界。我还有其他两种方法,一种使用inverse clipping技术和其他使用border segments技术。

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

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));
        }
    }
}

标题边框.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;
}

对于简单布局

好的,上述方法对于布局来说可能有点大材小用了doesn't有动态背景(如渐变、图像等)。对于简单的布局,正如@Slaw 在第一条评论中提到的,设置-fx-background-color: inherit;在父级和标签上都应该可以解决问题。

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;
}

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

.titled-border-simple > .title-label{
    -fx-background-color:inherit;
    -fx-border-color:inherit;
    -fx-border-width:inherit;
    -fx-border-radius:inherit;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JavaFX使节点覆盖父节点边框颜色 的相关文章

随机推荐

  • 检查 Objective-C 块类型?

    这主要是出于好奇 我不太确定它的实际用途是什么 但就这样吧 由于块也是 Objective C 对象 是否可以检查它们的类型 也就是说 它是否响应isKindOfClass 消息以及如何使用该消息来处理块 我天真的以为事情大概是这样的 vo
  • 在 Web 浏览器中禁用 F5 [重复]

    这个问题在这里已经有答案了 可能的重复 禁用浏览器的后退按钮 https stackoverflow com questions 961188 disable browsers back button 如何禁用浏览器上的 F5 刷新 htt
  • 使用 DiffUtil 在 RecyclerView 上添加拖放

    我有一个从房间数据库更新的列表 我从 Room 收到更新的数据作为新列表 然后将其传递给列表适配器 https developer android com reference androidx recyclerview widget Lis
  • 角度垫排序不适用于带点表示法的 matColumnDef

    我正在尝试按列对表进行排序 当我必须过滤另一个结果中的结果时 就会出现问题 我尝试通过括号表示法和点表示法访问该属性 但没有给出结果 还将最终节点放置在 matColumnDef 中 但失败 因为有 2 列同名 table table
  • 将 Indentity(自动增量)添加到现有表的主键[重复]

    这个问题在这里已经有答案了 可能的重复 向现有列添加标识 https stackoverflow com questions 1049210 adding an identity to an existing column 如何设置建表后自
  • 如何在 jQuery 中将标题转换为 URL slug?

    我正在 CodeIgniter 中开发一个应用程序 我试图在表单上创建一个字段来动态生成URL slug 我想做的是删除标点符号 将其转换为小写 然后用连字符替换空格 例如 Shane s Rib Shack 将变成 shanes rib
  • 仅在 Chrome 上我收到此错误:Uncaught TypeError: Illegal constructor [关闭]

    Closed 这个问题是无法重现或由拼写错误引起 help closed questions 目前不接受答案 当我在 Chrome 上加载 jQuery 时 我会收到此错误 Uncaught TypeError Illegal constr
  • `git ls-files -s` 输出中不同字段的含义是什么?

    在 Git 中 命令返回的典型结果行git ls files s好像 100755 be2c2e9b0966253096472d4b482c458bc892e493 0 gitignore 这些字段是什么意思 不用再犹豫了git ls fi
  • 使用 Jquery 传递隐藏字段值

    我有一个正常的hidden Input field我在哪里生成random string 我需要将其附加到我尝试将数据发布到另一个页面的 URL 中 我已经做到了这一点并且效果很好 url Upload html field1 newval
  • Erlang 如何睡觉(晚上?)

    我想在 Erlang 服务器上每隔几个小时运行一次小型清理过程 我知道计时器模块 我在教程中看到一个示例 使用链式计时器 睡眠命令来等待几天后发生的事件 我觉得这很奇怪 我知道 Erlang 进程与其他语言中的进程相比是独一无二的 但是进程
  • iOS Swift 和 reloadRowsAtIndexPaths 编译错误

    我与 xCode Swift 陷入僵局并刷新 UITableView 的单行 这条线有效 self tableView reloadData 而这条线没有 self tableView reloadRowsAtIndexPaths curr
  • 在球体边缘绘制点

    因此 来自 Flash 背景的我对一些简单的 2D 三角函数有很好的理解 在带有 I 圆的二维中 我知道使用给定角度和半径将项目放置在边缘上的数学 x cos a r y sin a r 现在 如果我在 3d 空间中有一个点 我知道球体的半
  • 删除重复的行并需要在mysql中保留所有行中的一个[重复]

    这个问题在这里已经有答案了 我想删除基于两列的重复行 但需要保留所有行 1 行 重复行可以多于两行 例如 ID NAME PHONE 1 NIL 1234 2 NIL 1234 3 NIL 1234 4 MES 5989 我想从上面 3 行
  • 重置 MySQL root 密码不起作用

    我花了很多时间阅读并尝试了数十种重置 root 密码的方法 但我一无所获 我发现 并尝试过 的最完整的说明如下 顺便说一句 我在 Win7 32 位上运行 MySQL 5 5 我创建了一个文件 c mysqlinit txt 其中包含两行
  • VBScript:从 Scripting.Dictionary 中对项目进行排序

    我有下面的代码 它获取这样的数据 姓名 1 姓名 4 姓名 2 姓名 3 并像这样列出 是一个复选框 姓名 1 姓名 4 姓名 2 姓名 3
  • 在 Go 中初始化嵌入结构

    我有以下内容struct其中包含一个net http Request type MyRequest struct http Request PathParams map string string 现在我想初始化匿名内部结构http Req
  • Android 26 (O) 通知不显示操作图标 [重复]

    这个问题在这里已经有答案了 随着 Android 26 O 引入通知渠道 我一直在调查 Google 提供的com example android notificationchannels 这个示例按预期工作 直到我尝试添加Action到示
  • 当从后台工作程序发生事件时,XlCall.Excel(XlCall.xlcCalculateNow) 抛出 XlCallException

    我有一个 ExcelFunction 来排队一些计算 ExcelFunction public static void QueueCalcs takes ranges var calcRequests builds list of calc
  • meteorjs 中的 imagemagick (在流星路由器和光纤的帮助下)

    我无法在meteorjs 中使用imagemagick 我正在开发一个小型 svg gt png 转换器 其中包含一个 REST API 来提供转换后的图像 我用meteor router实现了其余的api imagemagick 转换有效
  • JavaFX使节点覆盖父节点边框颜色

    我有一个如下所示的节点 仅使用 css 我希望标签覆盖其父边框颜色 因此标签下方的边框颜色部分变得不可见 我用来制作这个边框的CSS代码 fx border color black fx border width 3 fx border r