JavaFX应用程序的整体CSS样式

4
我们的应用程序在自定义CSS方面遇到了问题。目前为止,我们使用了一些解决方法来应用自定义CSS,但是保留了其他部分的Modena样式。
StyleManager.getInstance().addUserAgentStylesheet(css);

在8u40的Java更新中,此行为已受到限制,不再起作用。单独为所有场景设置样式表是疯狂的,可以通过设置自定义样式表来实现。
Application.setUserAgentStylesheet(css);

工作正常,但不是我们预期的方式 - Modena样式表被自定义样式表替换,因此许多控件根本没有样式。我们找到了另一种解决方法:将整个Modena文件夹复制到我们的项目中,将我们的自定义CSS样式添加到modena.css中,并(非常重要!)将modena.css重命名为其他名称,例如modenaB.css。在JavaFx源代码中,样式表会进行比较,如果名称保持不变,则使用原始的Modena样式表,即使提供了不同的URL。

我的问题是,在应用程序初始化时是否有更好的方法来应用一个自定义样式表,该样式表将在所有场景中用于整个应用程序,同时保留默认的Modena样式表以覆盖所有未覆盖的控件?

4个回答

2

把与构建场景或控制(视图)相关的所有逻辑放到单独的类中,然后用它来构建和分配适当的样式表,如何?

例如,在我的情况下,我创建了FXMLBuilder类。它只是根据控制器类加载FXML,然后遍历控制器层次结构并应用与相应控制器相关的样式表。在文件系统中,我有以下层次结构:

|
+- controllers
|  |
|  +- BaseController.java
|  +- BaseController.fxml
|  +- BaseController.css
|
...
|
+- MyController.java (inherited from BaseController)
+- MyController.fxml
+- MyController.css

所以,当我构建MyController的实例时,
MyController myController = new MyController(...);
Parent myControllerRootNode = FXMLBuilder.build(myController);
...

这里是关于IT技术的翻译内容:应用程序从MyController.css和BaseController.css文件中应用样式表。

以下是我的FXMLBuilder

public class FXMLBuilder {

    public static final String FXML_EXTENSION = "fxml";
    public static final String CSS_EXTENSION = "css";

    public static Parent build(Object controller) throws IOException {
        Class<?> controllerClass = controller.getClass();

        URL fxmlURL = getResourceURL(controllerClass, FXML_EXTENSION);

        FXMLLoader fxmlLoader = new FXMLLoader(fxmlURL);
        fxmlLoader.setControllerFactory(param -> controller);

        Parent rootNode = fxmlLoader.load();

        loadStylesheets(rootNode, controllerClass);

        return rootNode;
    }

    private static void loadStylesheets(Parent rootNode, Class<?> controllerClass) {
        Class<?> currentControllerClass = controllerClass;
        while (!currentControllerClass.equals(Object.class)) {
            if (getResourceURL(currentControllerClass, CSS_EXTENSION) != null) {
                rootNode.getStylesheets().add(getResourcePath(currentControllerClass, CSS_EXTENSION));
            }
            currentControllerClass = currentControllerClass.getSuperclass();
        }
    }

    public static String getResourcePath(Class<?> resourceClass, String extension) {
        return String.format("/%s.%s", resourceClass.getCanonicalName().replace(".", "/"), extension);
    }

    public static URL getResourceURL(Class<?> resourceClass, String extension) {
        return resourceClass.getResource(getResourcePath(resourceClass, extension));
    }

}

我认为你可以编写类似以下代码的内容,它将加载适用于所有视图的基础样式表,然后加载指定视图的自定义样式表。

另外,最好从基础控制器开始迭代整个层次结构。


这似乎是一个非常有趣的解决方案,我会尝试使用它。谢谢。 - user3886894

2
最简单的方法是将你的CSS文件添加到用户代理样式表中。
@Override
public void start(Stage primaryStage) {
    // ...
    Application.setUserAgentStylesheet(Application.STYLESHEET_MODENA);
    StyleManager.getInstance().addUserAgentStylesheet("example.css");
    // ...
}

Answer inspired from GuiGarage.


0
如果您是涉及的CSS文件的作者,您可以在CSS文件顶部添加一个导入语句。这样您就可以将它用作userAgentStyleSheet替代品。例如:
@import "com/sun/javafx/scene/control/skin/modena/modena.css";
/* rest of CSS code */

如果您无法控制相关的 CSS 文件,您可以创建一个“包装器”CSS 文件,在彼此之后导入 Modena 和其他 CSS 文件。例如:

@import "com/sun/javafx/scene/control/skin/modena/modena.css";
@import "the/extra/file.css";

在您的应用程序中,您可以使用以下方式设置userAgentStyleSheet:
Application.setUserAgentStylesheet(css);

0

从我的经验来看,最简单的方法是在SceneBuilder中设置顶级父节点的CSS。该节点的所有子节点都将继承此CSS。然后对于单个情况,您可以加载另一个CSS以覆盖您想要的视图。我不知道它是否在代码中以同样的方式工作,但在SceneBuilder中确实如此!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接