3 回答
TA贡献2039条经验 获得超7个赞
你很幸运,因为唯一的区别是颜色。您可以为此目的使用查找颜色:my-color-name: <value>;对节点本身或祖先之一使用规则。这允许您指定这些内联 css 值并在 CSS 样式表中使用它们:
@Override
public void start(Stage primaryStage) {
HBox hBox = new HBox();
hBox.setMaxHeight(Region.USE_PREF_SIZE);
hBox.getStyleClass().add("box");
StackPane root = new StackPane(hBox);
Stream.of("red", "green", "blue").map(c -> {
Button b = new Button(c);
b.setOnAction(evt -> {
root.setStyle("-my-background:" + c);
});
return b;
}).forEach(hBox.getChildren()::add);
Scene scene = new Scene(root, 500, 500);
scene.getStylesheets().add(getClass().getResource("/path/to/my/style.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
CSS 样式表
.box {
-fx-background-color: -my-background;
}
您可以通过这种方式指定多种颜色,例如
root.setStyle("-my-color: red; -some-other-color: brown; -color-3: yellow;");
TA贡献1821条经验 获得超4个赞
如果您只想更改颜色而不修改 CSS 的其余部分,您也可以遵循以下方法。如果您有一个非常大的 css 文件需要主题化,这会非常有用。
基本思想是让所有 css 都是一个基本 css 文件。将所有颜色定义为该基本文件中 .root 类中的变量。对于每个主题 css,您只需要覆盖颜色变量即可。并在基本文件之上加载主题 css 文件。这样您就不会遇到任何可能的复制粘贴问题或缺少 css 问题:)
一个完整的工作示例如下:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.stream.Stream;
public class DynamicStyling_Demo extends Application {
@Override
public void start(Stage stage) throws Exception {
VBox root = new VBox();
root.setAlignment(Pos.CENTER);
root.setSpacing(10);
Stream.of("Default", "Type1", "Type2", "Type3").forEach(type -> {
Button button = new Button("Open " + type);
button.setOnAction(e -> {
Stage subStage = buildStage(type);
subStage.initOwner(stage);
if (!type.equalsIgnoreCase("default")) {
subStage.getScene().getStylesheets().add(this.getClass().getResource(type.toLowerCase() + ".css").toExternalForm());
}
subStage.show();
});
root.getChildren().add(button);
});
Scene sc = new Scene(root, 400, 400);
sc.getStylesheets().add(this.getClass().getResource("base.css").toExternalForm());
stage.setScene(sc);
stage.show();
}
private Stage buildStage(String title) {
Label label = new Label("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
label.setWrapText(true);
VBox.setVgrow(label, Priority.ALWAYS);
Button btn = new Button("Sample Button");
VBox pane = new VBox(label, btn);
pane.getStyleClass().add("my-pane");
StackPane subRoot = new StackPane(pane);
subRoot.setPadding(new Insets(10));
Stage subStage = new Stage();
subStage.setTitle(title);
subStage.setScene(new Scene(subRoot, 300, 300));
subStage.getScene().getStylesheets().add(this.getClass().getResource("base.css").toExternalForm());
return subStage;
}
public static void main(String[] args) {
Application.launch(args);
}
}
基本.css:
.root{
-fx-window-border: #444444;
-fx-window-color: #999999;
-fx-window-text: #111111;
-fx-button-color: #555555;
}
.my-pane{
-fx-border-width: 2px;
-fx-border-color: -fx-window-border;
-fx-background-color: -fx-window-color;
-fx-padding: 10px;
-fx-spacing: 10px;
}
.my-pane .label{
-fx-text-fill: -fx-window-text;
-fx-font-size: 16px;
}
.my-pane .button{
-fx-base: -fx-button-color;
}
类型1.css:
.root{
-fx-window-border: red;
-fx-window-color: yellow;
-fx-window-text: brown;
-fx-button-color: pink;
}
类型2.css:
.root{
-fx-window-border: green;
-fx-window-color: lightblue;
-fx-window-text: white;
-fx-button-color: grey;
}
类型3.css:
.root{
-fx-window-border: brown;
-fx-window-color: lightgreen;
-fx-window-text: blue;
-fx-button-color: yellow;
}
TA贡献1862条经验 获得超7个赞
以下是动态设置给定组件主题颜色的几个具体示例:
费边对查找颜色的建议。
以编程方式创建的 CSS 样式表写入临时文件。
使用查找颜色的示例
该示例正在做的是设置一些标准查找颜色,这些颜色可以为modena.css
您想要设置样式的三件事设置样式:
背景颜色 (
-fx-background-color
)。这是从该类派生的类中使用的标准背景Pane
。文本的颜色 (
-fx-text-background-color
)。是的,我知道这个名字很令人困惑,但无论出于什么原因,它似乎就是这样。按钮的颜色 (
-fx-base
)。
在示例应用程序中,用户可以使用 JavaFXColorPicker控件动态选择颜色来修改预览窗格中显示的项目的颜色。
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class ThemeMaker extends Application {
@Override
public void start(Stage stage) throws Exception {
Pane previewPane = createPreviewPane();
Pane controlPane = createControlPane(previewPane);
Pane layout = new VBox(
20,
controlPane,
previewPane
);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
private Pane createControlPane(Pane previewPane) {
ColorPicker backgroundColorPicker = new ColorPicker(Color.web("#b3ccff"));
ColorPicker textColorPicker = new ColorPicker(Color.web("#4d804d"));
ColorPicker controlColorPicker = new ColorPicker(Color.web("#ffe6cc"));
GridPane controlPane = new GridPane();
controlPane.setHgap(5);
controlPane.setVgap(5);
controlPane.addRow(0, new Label("Background color:"), backgroundColorPicker);
controlPane.addRow(1, new Label("Text color:"), textColorPicker);
controlPane.addRow(2, new Label("Control color:"), controlColorPicker);
backgroundColorPicker.valueProperty().addListener((observable, oldColor, newColor) ->
setThemeColors(previewPane, backgroundColorPicker.getValue(), textColorPicker.getValue(), controlColorPicker.getValue())
);
textColorPicker.valueProperty().addListener((observable, oldColor, newColor) ->
setThemeColors(previewPane, backgroundColorPicker.getValue(), textColorPicker.getValue(), controlColorPicker.getValue())
);
controlColorPicker.valueProperty().addListener((observable, oldColor, newColor) ->
setThemeColors(previewPane, backgroundColorPicker.getValue(), textColorPicker.getValue(), controlColorPicker.getValue())
);
setThemeColors(previewPane, backgroundColorPicker.getValue(), textColorPicker.getValue(), controlColorPicker.getValue());
return controlPane;
}
private void setThemeColors(Pane previewPane, Color backgroundColor, Color textColor, Color controlColor) {
previewPane.setStyle(
"-fx-background-color: " + toHexString(backgroundColor) + ";" +
"-fx-text-background-color: " + toHexString(textColor) + ";" +
"-fx-base: " + toHexString(controlColor) + ";"
);
}
private Pane createPreviewPane() {
Label label = new Label(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, " +
"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
label.setWrapText(true);
Button btn = new Button("Sample Button");
Pane previewPane = new VBox(10, label, btn);
previewPane.setPadding(new Insets(5));
previewPane.setPrefWidth(200);
return previewPane;
}
// from https://stackoverflow.com/a/56733608/1155209 "How to get hex web String from JavaFX ColorPicker color?"
private String toHexString(Color value) {
return "#" + (format(value.getRed()) + format(value.getGreen()) + format(value.getBlue()) + format(value.getOpacity()))
.toUpperCase();
}
private String format(double val) {
String in = Integer.toHexString((int) Math.round(val * 255));
return in.length() == 1 ? "0" + in : in;
}
public static void main(String[] args) {
launch(args);
}
}
使用动态样式表的示例
更新
JavaFX 17 添加了从数据 URI 加载样式表的功能。这将是下面示例中演示的另一种方法,该方法使用文件 IO 动态创建 CSS 文件。
因此,查找颜色解决方案非常强大,因为您可以动态设置场景中所有项目的颜色样式。然而,CSS 一般来说比颜色设置更强大。如果您的动态样式需要的不仅仅是颜色设置,或者您需要非常具体的规则来设置场景中特定项目的样式,那么您将需要自己的自定义样式表。
stylesheets节点和场景的属性是一个动态的可观察列表。因此,如果更改样式表,您将重新设置节点或场景的样式。每个样式表都由一个 URL 引用。因此,要动态创建样式表,您需要做的就是在代码中构造样式表内容并将其写入临时文件,获取对临时文件的 URL 引用,然后将其设置为您想要的样式表风格。
要提供此方法的示例,只需使用上一个示例中使用查找颜色的代码,并将 setThemeColors 方法替换为以下方法。然后,这将使用动态创建的 CSS 文件而不是查找的颜色来完成预览窗格的动态样式。
注意:在创建动态样式表时,我尝试使用选择.root器来定义样式(类似于 Sai 的答案),但是,无论出于何种原因,它都不起作用(也许它不适用于我的 JavaFX (v13) 版本) 。因此,我使用特定的 CSS 选择器来设置项目的样式(例如label{-fx-text-fill:<custom-color>})。这工作得很好,而且作为奖励,它展示了通过定义自己的样式表可以获得的额外控制级别。
private void setThemeColors(Pane previewPane, Color backgroundColor, Color textColor, Color controlColor) {
try {
Path cssPath = Files.createTempFile("fx-theme-", ".css");
Files.writeString(
cssPath,
".themed{-fx-background-color:"+ toHexString(backgroundColor) +";}" +
".label{-fx-text-fill:"+ toHexString(textColor) +";}" +
".button{-fx-base:" + toHexString(controlColor) + ";}"
);
cssPath.toFile().deleteOnExit();
System.out.println("Wrote " + cssPath);
System.out.println("URL " + cssPath.toUri().toURL().toExternalForm());
previewPane.getStyleClass().setAll("themed");
previewPane.getStylesheets().setAll(
cssPath.toUri().toURL().toExternalForm()
);
} catch (IOException e) {
e.printStackTrace();
}
}
查找颜色的背景
以下文档是从有关查找颜色的链接 JavaFX CSS 参考部分复制的。这是一种实现您愿望的强大技术,并且该概念(据我所知)是 JavaFX CSS 处理所特有的,并且在基于标准 HTML 的 CSS 中不存在。
通过查找颜色,您可以引用在当前节点或其任何父节点上设置的任何其他颜色属性。这是一个非常强大的功能,因为它允许在场景上指定通用颜色调色板,然后在整个应用程序中使用。如果您想更改其中一种调色板颜色,您可以在场景树中的任何级别执行此操作,这将影响该节点及其所有后代。查找到的颜色在应用之前不会被查找,因此它们是实时的,并对可能发生的任何样式更改做出反应,例如在运行时用节点上的“样式”属性替换调色板颜色。
如果您在您正在使用的 JavaFX SDK 附带的 jar 文件中进行搜索,您将找到一个名为modena.css. 该文件预定义了许多查找的颜色,您可以覆盖这些颜色,以便更轻松地为应用程序设置主题。这些没有在任何地方记录,您需要查看 modena.css 文件以了解它们是什么(最有用的文件位于.root文件的 部分)。最重要的颜色-fx-base将为整个 JavaFX 控制系统设置基色。
查找的颜色通常与其他一些 JavaFX CSS 概念(例如派生和阶梯)结合起来,以创建一致的主题,当基本查找颜色发生变化时,这些主题仍然可读。这允许您更改基色,例如从白色更改为黑色,并且基于基色的控件中显示的文本将自动从回更改为白色,以便仍然可读。
什么是-fx-base?
fx-base是所有控件的基色,因此设置它会更改场景中所有控件的颜色,这可能也是您想要的,但也许不是。
如果您只想更改按钮而不是场景中的所有内容,只需直接在每个按钮上而不是在封闭的窗格上设置 -fx-base 颜色即可。实现这一目标的一种棘手方法是,您可以为按钮定义自己的 CSS 样式,然后在程序中将样式-fx-base: my-custom-color设置为动态值,如 fabian 的答案所示。my-custom-color
请注意,设置基色优于尝试设置实际按钮颜色。因为,当您仔细观察时,按钮本身包括从基色派生的各种渐变和阴影,因此在渲染时它实际上是由多种颜色组成的,而不仅仅是单一颜色。
添加回答
举报