【问题标题】:How to parse/map JavaFX CSS file to retrieve its properties and values?如何解析/映射 JavaFX CSS 文件以检索其属性和值?
【发布时间】:2017-07-25 09:16:03
【问题描述】:

我的应用程序允许用户使用自定义 CSS 主题来设置界面样式。我有几个预先构建的“主题”可供选择,它们非常简单,只有 3 个属性。

示例 CSS:

.root{
    -fx-background: #325c81;
    -fx-default-button: #77a3ca;
    -fx-base: #a7c4dd;
}

应用程序有 3 个ColorPicker 控件,需要允许用户为每个属性选择颜色并保存回 CSS 文件。

我在实际编写 CSS 文件时没有问题,但我找不到解析 .css 文件的方法,以便使用 .css 文件中的值设置 ColorPicker 控件的值。

基本程序流程

1) 用户从ComboBox 中选择一个预制主题:

cboPresetTheme.valueProperty().addListener((observable, priorTheme, newTheme) -> {
                Utility.applyTheme(cboPresetTheme.getScene(), newTheme);
            });

2) 关联的.css文件被加载并应用到当前Scene

public static void applyTheme(Scene scene, Theme theme) {
    scene.getStylesheets().clear();

    File css = new File("themes/" + theme.getFileName());
    File fontFile = new File("themes/Font.css");

    scene.getStylesheets().addAll(
            css.toURI().toString(),
            fontFile.toURI().toString());
}

3) 3 个ColorPicker 控件使用来自应用的StyleSheet 的值进行更新:

cpBackground.setValue(Color.valueOf(cssFileBackground));
cpBase.setValue(Color.valueOf(cssFileBase));
cpDefaultButton.setValue(Color.valueOf(cssFileDefaultButton));

虽然我对第 1 步和第 2 步没有问题,但我不知道如何处理第 3 步。

我查看了其他 CSS Parser 库(谢谢,Google),但它们似乎更适合标准 CSS 并且不支持 FX 属性。 StackExchange 问题edit or parse FX-CSS file programmatically 似乎在问同样的问题,但从未成功回答。

一个答案建议使用CSS Parser 来完成此操作,但由于文档很少(以及超出我目前的理解水平的内容),我不知道从哪里开始。

我知道目前可能没有标准 API 可用于完成此操作,但我希望可能有一个我无法找到的简单库或解决方案。

【问题讨论】:

    标签: java css parsing javafx


    【解决方案1】:

    您可以通过多种方式将 CSS 声明转换为颜色。

    设置辅助节点样式

    这很简单,但很有效:这个想法是,您可以使用相同的 css 设置节点的背景颜色,然后使用该颜色设置 colorPicker 值。

    在这种情况下,您唯一需要考虑的是节点的样式在添加到场景时。

    所以你必须将节点添加到场景中。添加大小为 0x0 的节点不会导致任何问题,但可能您不希望它存在,因此您可以使用辅助场景。

    public class CSSParsingApp extends Application {
    
        @Override
        public void start(Stage primaryStage) {
            ColorPicker cpBackground = new ColorPicker(retrieveColor("value1"));
            ColorPicker cpBase = new ColorPicker(retrieveColor("value2"));
            ColorPicker cpDefaultButton = new ColorPicker(retrieveColor("value3"));
    
            VBox root = new VBox(10, cpBackground, cpDefaultButton, cpBase);
            root.setAlignment(Pos.CENTER);
    
            Scene scene = new Scene(root, 300, 250);
            scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
    
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        private Color retrieveColor(String value) {
            Pane pane = new Pane();
            pane.getStyleClass().add(value);
    
            Scene sceneAux = new Scene(pane);
            sceneAux.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
            pane.applyCss();
            return (Color) pane.getBackground().getFills().get(0).getFill();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
    }
    

    style.css 在哪里:

    .root {
        -fx-background: #325c81;
        -fx-default-button: #77a3ca;
        -fx-base: #a7c4dd;
    }
    
    .value1 {
        -fx-background-color: -fx-background;
    }
    .value2 {
        -fx-background-color: -fx-default-button;
    }
    .value3 {
        -fx-background-color: -fx-base;
    }
    

    使用 StylableProperties

    here 中找到了一个类似的、更优雅的解决方案。它使用StylableProperties 创建一个节点,您可以使用自定义-named-color 属性设置该节点的样式,然后将此helper 节点添加到主场景中。

    基本上和上面的想法一样,可能更干净,因为你不需要修改你的css文件。

    使用CssToColorHelper,您的代码将如下所示:

    public class CSSParsingApp extends Application {
    
        private CssToColorHelper helper = new CssToColorHelper();
    
        @Override
        public void start(Stage primaryStage) {
            ColorPicker cpBackground = new ColorPicker();
            ColorPicker cpBase = new ColorPicker();
            ColorPicker cpDefaultButton = new ColorPicker();  
    
            VBox root = new VBox(10, cpBackground, cpDefaultButton, cpBase, helper);
            root.setAlignment(Pos.CENTER);
    
            Scene scene = new Scene(root, 300, 250);
            scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
    
            cpBackground.setValue(getNamedColor("-fx-background"));
            cpDefaultButton.setValue(getNamedColor("-fx-default-button"));
            cpBase.setValue(getNamedColor("-fx-base"));
    
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        private Color getNamedColor(String name) {
            helper.setStyle("-named-color: " + name + ";");
            helper.applyCss();
    
            return helper.getNamedColor();      
        }
        /**
         * @param args the command line arguments
         */
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    style.css 是您的 css 文件:

    .root {
        -fx-background: #325c81;
        -fx-default-button: #77a3ca;
        -fx-base: #a7c4dd;
    }
    

    使用 JavaFX CSSParser

    如果您正在寻找 CSSParser,为什么不直接使用 JavaFX 中包含的那个,即您实际用于将样式应用到您的应用程序的那个?

    它在com.sun.javafx.css.parser.CSSParser 下,如果答案是你不想使用私有API,好消息是它将是JavaFX 9 中的公共API。

    有了它,您可以解析 css 文件并轻松检索任何解析后的值。

     public class CSSParsingApp extends Application {
    
        @Override
        public void start(Stage primaryStage) {
            ColorPicker cpBackground = new ColorPicker();
            ColorPicker cpBase = new ColorPicker();
            ColorPicker cpDefaultButton = new ColorPicker();  
    
            VBox root = new VBox(10, cpBackground, cpDefaultButton, cpBase);
            root.setAlignment(Pos.CENTER);
    
            Scene scene = new Scene(root, 300, 250);
            scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
    
            cpBackground.setValue(parseColor("-fx-background"));
            cpDefaultButton.setValue(parseColor("-fx-default-button"));
            cpBase.setValue(parseColor("-fx-base"));
    
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        private Color parseColor(String property) {
            CSSParser parser = new CSSParser();
            try {
                Stylesheet css = parser.parse(getClass().getResource("style.css").toURI().toURL());
                final Rule rootRule = css.getRules().get(0); // .root
                return (Color) rootRule.getDeclarations().stream()
                    .filter(d -> d.getProperty().equals(property))
                    .findFirst()
                    .map(d -> ColorConverter.getInstance().convert(d.getParsedValue(), null))
                    .get();
            } catch (URISyntaxException | IOException ex) { }
            return Color.WHITE;
        }
    
        /**
         * @param args the command line arguments
         */
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    style.css 是你的 css 文件:

    .root {
        -fx-background: #325c81;
        -fx-default-button: #77a3ca;
        -fx-base: #a7c4dd;
    }
    

    【讨论】:

    • 使用内置的 CSSParser 看起来很有希望,谢谢!但是,我在Stylesheet css = parser.parse(Utility.class.getResource(cssFile).toURI().toURL());' My cssFile 上得到了一个NullPointerException,它作为字符串传递给了有问题的.css 文件。
    • 确保您在与Utility 类相同的路径下有一个有效的cssFile。还要在 catch 中放置一个ex.printStackTrace(),这样您就可以找出该异常的原因。
    • 我假设它与 Utility.class 为空有关(字符串检查正常)。我将解析器放在Static 方法中,所以我不能使用getClass()
    • cssFile 字符串包含文件的完整相对路径;那不行吗?而e.printStackTrace() 只会把我带到那条线。有没有办法找出导致异常的行的哪一部分?
    • 我建议你只使用css文件的名称,并将其放在与Utility类相同的包路径下。或者使用绝对路径,使用"/" + cssFile作为获取资源的路径。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多