我的应用程序允许用户使用自定义CSS主题来设置界面样式。我有几个预构建的“主题”可供选择,它们非常简单,只有3个属性。
CSS示例:
.root{
-fx-background: #325c81;
-fx-default-button: #77a3ca;
-fx-base: #a7c4dd;
}
该应用程序具有3个
ColorPicker
控件,这些控件需要允许用户为每个属性选择一种颜色并将其保存回CSS文件。我实际上写CSS文件没有问题,但是我找不到解析
.css
文件的方法,以便用ColorPicker
文件中的值设置.css
控件的值。基本程序流程
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解析器库(谢谢Google),但它们似乎更适合站立式CSS,并且不支持FX属性。 StackExchange问题edit or parse FX-CSS file programmatically似乎在问相同的问题,但从未成功回答。
一个答案建议使用CSS Parser来完成此操作,但是由于几乎不了解文档(而且超出了我目前的理解水平),所以我不知道从哪里开始。
我知道目前可能没有标准的API可以完成此任务,但是我希望那里可能有一个我找不到的简单库或解决方案。
最佳答案
有几种方法可以解决将CSS声明转换为Color的问题。
为辅助节点设置样式
这很简单,但是很有效:想法是您可以使用相同的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;
}