Javafx - 在活动 window 上更改主题 (CSS)
Javafx - change Theme (CSS) on active window
我想要一个允许我在 Dark/Light-mode 之间切换的按钮。
但我有问题如果我切换,活动 windows 不会改变他们的风格。
第一个代码片段可以轻松重现问题。
对于 Maven 项目启动器 class:
package testapp;
//This Class is Required in mavenproject to Start the Application
public class GUIStarter {
public static void main(final String[] args) {
try{
TestApp.main(args);
}catch (Exception e){
System.out.println("GUIStarter Errror:\n"+e);
}
}
}
小学Windowclass:
public class TestApp extends Application {
//stylepaths
public static String mainLightModePath = ".\style_lightmode.css";
public static String mainDarkModePath = ".\style-Darkmode.css";
public static boolean isItDarkmode = true;
public static void toggleMode(){
if(isItDarkmode){
isItDarkmode = false;
}else if(!isItDarkmode){
isItDarkmode = true;
}
}
//This is required to get the primary Stage in other Stages (Controllers)
private static Stage pStage;
public static Stage getPrimaryStage() {
return pStage;
}
private void setPrimaryStage(Stage pStage) {
TestApp.pStage = pStage;
}
public void setPrimaryWindow(Stage primaryStage){
try{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Mainframe.fxml"));
Parent root = (Parent) fxmlLoader.load();
Scene scene = new Scene(root);
primaryStage.setTitle("TestApp");
//scene.setMoveControl(titleBar);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e){
e.printStackTrace();
}
}
//Start of Application
@Override
public void start(Stage primaryStage) {
setPrimaryStage(primaryStage);
pStage = primaryStage;
setPrimaryWindow(primaryStage);
}
public static void main(String[] args) {
launch(args);
}
}
主控Class:
public class Mainframe {
public static Timeline time;
@FXML
public void options(ActionEvent event){
try{
TestApp.toggleMode();
Stage stage = (Stage) ((Node)event.getSource()).getScene().getWindow();
Stage changer = TestApp.getPrimaryStage();
stage.close();
changer.hide();
//My idea was to initialize again and load the styles in that way again but does not change anything
FXMLLoader loader = new FXMLLoader(getClass().getResource("Mainframe.fxml"));
loader.load();
Mainframe ctrl = loader.getController();
ctrl.initialize();
changer.show();
}catch (Exception e) {
System.out.println("Error bei der App Klicken von ModeButton. \n error is: "+e);
e.printStackTrace();
}
}
@FXML
public BorderPane primaryparent;
//get and set for elements
@FXML
void initialize() {
//Check Theme
if(TestApp.isItDarkmode){
primaryparent.getStylesheets().clear();
primaryparent.getStylesheets().add(getClass().getResource(TestApp.mainDarkModePath).toString());
}else if(!TestApp.isItDarkmode){
primaryparent.getStylesheets().clear();
primaryparent.getStylesheets().add(getClass().getResource(TestApp.mainLightModePath).toString());
}
}
}
Mainframe.fxml
:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.text.Font?>
<BorderPane fx:id="primaryparent" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="0.0" minWidth="0.0" prefHeight="224.0" prefWidth="515.0" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="testapp.Mainframe">
<center>
<AnchorPane prefHeight="200.0" prefWidth="200.0" styleClass="rootBackground" BorderPane.alignment="CENTER">
<children>
<Button fx:id="setDarkModeButton" layoutX="175.0" layoutY="99.0" mnemonicParsing="false" onAction="#options" styleClass="settingsButton" text="toggle Dark/LightMode">
<font>
<Font name="Arial" size="13.0" />
</font>
<padding>
<Insets bottom="5.0" left="17.0" right="17.0" top="5.0" />
</padding>
</Button>
</children>
</AnchorPane>
</center>
</BorderPane>
之前我只是 overwrote
“style.css”文件并使用了 .stop()
和 .show()
。这很好用。但是你拥有的 css-files
越多,它就会变得越复杂,当它被打包到一个 .jar
文件后,它就不再工作了。
我希望有人能帮助我。因为现在我真的没有想法了...
更新:
我想从另一个控制器控制它:
package testapp;
public class Mainframe {
public static Timeline time;
@FXML Button setDarkModeButton;
@FXML
public void options(ActionEvent event){
try{
try{
Stage secSTAGE = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(TestApp.class.getResource("Mainframe2.fxml"));
Parent root = (Parent) fxmlLoader.load();
Scene scene = new Scene(root);
secSTAGE.setTitle("TestAppw2");
//scene.setMoveControl(titleBar);
secSTAGE.setScene(scene);
secSTAGE.show();
} catch (Exception e){
e.printStackTrace();
}
}catch (Exception e) {
System.out.println("Error bei der App Klicken von ModeButton. \n error is: "+e);
e.printStackTrace();
}
}
@FXML
public BorderPane primaryparent;
//get and set for elements
@FXML
void initialize() {
//Check Theme
if(TestApp.isItDarkmode){
primaryparent.getStylesheets().clear();
primaryparent.getStylesheets().add(getClass().getResource(TestApp.mainDarkModePath).toString());
primaryparent.getStylesheets().add(getClass().getResource(TestApp.mainLightModePath).toString());
}
}
}
public class Mainframe2 {
public static Timeline time;
@FXML Button setDarkModeButton;
@FXML
public void options(ActionEvent event){
try{
TestApp.toggleMode();
Stage stage = (Stage) ((Node)event.getSource()).getScene().getWindow();
FXMLLoader loader = new FXMLLoader(getClass().getResource("Mainframe.fxml"));
Parent root = (Parent) loader.load();
Mainframe ctrl = loader.getController();
//setDarkModeButton.getStylesheets().set(0, "style_lightmode.css");
if(TestApp.isItDarkmode){
root.getStylesheets().set(0, Mainframe.class.getResource(TestApp.mainDarkModePath).toString());
}else{
root.getStylesheets().set(0, Mainframe.class.getResource(TestApp.mainLightModePath).toString());
}
}catch (Exception e) {
System.out.println("Error bei der App Klicken von ModeButton. \n error is: "+e);
e.printStackTrace();
}
}
@FXML
public BorderPane primaryparent;
//get and set for elements
@FXML
void initialize() {
//Check Theme
if(TestApp.isItDarkmode){
primaryparent.getStylesheets().clear();
primaryparent.getStylesheets().add(getClass().getResource(TestApp.mainDarkModePath).toString());
}else if(!TestApp.isItDarkmode){
primaryparent.getStylesheets().clear();
primaryparent.getStylesheets().add(getClass().getResource(TestApp.mainLightModePath).toString());
}
}
}
看到那种没人对我的问题感兴趣的。
这不是我想要的,但只要没有人有更好的解决方案:
您可以关闭旧阶段并使用新阶段创建新阶段 Theme-style。
所以在TestApp.class
中必须设置setPrimaryWindow()
方法public
和static
。所以你不能使用 .getClass()
并且 pStage 必须设置为 setPrimaryWindow()
:
中的新 Stage
public static void setPrimaryWindow(Stage primaryStage){
try{
pStage = primaryStage;
FXMLLoader fxmlLoader = new FXMLLoader(TestApp.class.getResource("Mainframe.fxml"));
Parent root = (Parent) fxmlLoader.load();
Scene scene = new Scene(root);
primaryStage.setTitle("TestApp");
//scene.setMoveControl(titleBar);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e){
e.printStackTrace();
}
}
@Override
public void start(Stage primaryStage) {
setPrimaryStage(primaryStage);
setPrimaryWindow(primaryStage);
}
在选项 maincontroller.class
中,您只需 close()
旧的 window 并调用 setPrimaryWindow()
:
@FXML
public void options(ActionEvent event){
try{
TestApp.toggleMode();
Stage stage = (Stage) ((Node)event.getSource()).getScene().getWindow();
Stage changer = TestApp.getPrimaryStage();
stage.close();
changer.close();
//create new Window on Theme switch
Stage nStage = new Stage();
TestApp.setPrimaryWindow(nStage);
}catch (Exception e) {
System.out.println("Error bei der App Klicken von ModeButton. \n error is: "+e);
e.printStackTrace();
}
}
Css 文件可以在 运行 上更改,无需重新加载或设置新阶段
用 set()
方法替换 observableList
的样式表
App.java
public class App extends Application {
@Override
public void start(Stage stage) {
Button button = new Button("change");
button.setOnAction(e ->button.getScene().getStylesheets().set(0, "1.css"));
Button button1 = new Button("default");
button1.setOnAction(e ->button.getScene().getStylesheets().set(0, "0.css"));
HBox hBox = new HBox(button,button1);
AnchorPane anchorPane =new AnchorPane(hBox);
anchorPane.getStyleClass().add("pane");
var scene = new Scene(anchorPane, 640, 480);
scene.getStylesheets().add("0.css");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
0.css
.pane{
-fx-background-color:cyan;
}
1.css
.pane{
-fx-background-color:orange;
}
我想要一个允许我在 Dark/Light-mode 之间切换的按钮。 但我有问题如果我切换,活动 windows 不会改变他们的风格。
第一个代码片段可以轻松重现问题。 对于 Maven 项目启动器 class:
package testapp;
//This Class is Required in mavenproject to Start the Application
public class GUIStarter {
public static void main(final String[] args) {
try{
TestApp.main(args);
}catch (Exception e){
System.out.println("GUIStarter Errror:\n"+e);
}
}
}
小学Windowclass:
public class TestApp extends Application {
//stylepaths
public static String mainLightModePath = ".\style_lightmode.css";
public static String mainDarkModePath = ".\style-Darkmode.css";
public static boolean isItDarkmode = true;
public static void toggleMode(){
if(isItDarkmode){
isItDarkmode = false;
}else if(!isItDarkmode){
isItDarkmode = true;
}
}
//This is required to get the primary Stage in other Stages (Controllers)
private static Stage pStage;
public static Stage getPrimaryStage() {
return pStage;
}
private void setPrimaryStage(Stage pStage) {
TestApp.pStage = pStage;
}
public void setPrimaryWindow(Stage primaryStage){
try{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Mainframe.fxml"));
Parent root = (Parent) fxmlLoader.load();
Scene scene = new Scene(root);
primaryStage.setTitle("TestApp");
//scene.setMoveControl(titleBar);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e){
e.printStackTrace();
}
}
//Start of Application
@Override
public void start(Stage primaryStage) {
setPrimaryStage(primaryStage);
pStage = primaryStage;
setPrimaryWindow(primaryStage);
}
public static void main(String[] args) {
launch(args);
}
}
主控Class:
public class Mainframe {
public static Timeline time;
@FXML
public void options(ActionEvent event){
try{
TestApp.toggleMode();
Stage stage = (Stage) ((Node)event.getSource()).getScene().getWindow();
Stage changer = TestApp.getPrimaryStage();
stage.close();
changer.hide();
//My idea was to initialize again and load the styles in that way again but does not change anything
FXMLLoader loader = new FXMLLoader(getClass().getResource("Mainframe.fxml"));
loader.load();
Mainframe ctrl = loader.getController();
ctrl.initialize();
changer.show();
}catch (Exception e) {
System.out.println("Error bei der App Klicken von ModeButton. \n error is: "+e);
e.printStackTrace();
}
}
@FXML
public BorderPane primaryparent;
//get and set for elements
@FXML
void initialize() {
//Check Theme
if(TestApp.isItDarkmode){
primaryparent.getStylesheets().clear();
primaryparent.getStylesheets().add(getClass().getResource(TestApp.mainDarkModePath).toString());
}else if(!TestApp.isItDarkmode){
primaryparent.getStylesheets().clear();
primaryparent.getStylesheets().add(getClass().getResource(TestApp.mainLightModePath).toString());
}
}
}
Mainframe.fxml
:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.text.Font?>
<BorderPane fx:id="primaryparent" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="0.0" minWidth="0.0" prefHeight="224.0" prefWidth="515.0" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="testapp.Mainframe">
<center>
<AnchorPane prefHeight="200.0" prefWidth="200.0" styleClass="rootBackground" BorderPane.alignment="CENTER">
<children>
<Button fx:id="setDarkModeButton" layoutX="175.0" layoutY="99.0" mnemonicParsing="false" onAction="#options" styleClass="settingsButton" text="toggle Dark/LightMode">
<font>
<Font name="Arial" size="13.0" />
</font>
<padding>
<Insets bottom="5.0" left="17.0" right="17.0" top="5.0" />
</padding>
</Button>
</children>
</AnchorPane>
</center>
</BorderPane>
之前我只是 overwrote
“style.css”文件并使用了 .stop()
和 .show()
。这很好用。但是你拥有的 css-files
越多,它就会变得越复杂,当它被打包到一个 .jar
文件后,它就不再工作了。
我希望有人能帮助我。因为现在我真的没有想法了...
更新: 我想从另一个控制器控制它:
package testapp;
public class Mainframe {
public static Timeline time;
@FXML Button setDarkModeButton;
@FXML
public void options(ActionEvent event){
try{
try{
Stage secSTAGE = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(TestApp.class.getResource("Mainframe2.fxml"));
Parent root = (Parent) fxmlLoader.load();
Scene scene = new Scene(root);
secSTAGE.setTitle("TestAppw2");
//scene.setMoveControl(titleBar);
secSTAGE.setScene(scene);
secSTAGE.show();
} catch (Exception e){
e.printStackTrace();
}
}catch (Exception e) {
System.out.println("Error bei der App Klicken von ModeButton. \n error is: "+e);
e.printStackTrace();
}
}
@FXML
public BorderPane primaryparent;
//get and set for elements
@FXML
void initialize() {
//Check Theme
if(TestApp.isItDarkmode){
primaryparent.getStylesheets().clear();
primaryparent.getStylesheets().add(getClass().getResource(TestApp.mainDarkModePath).toString());
primaryparent.getStylesheets().add(getClass().getResource(TestApp.mainLightModePath).toString());
}
}
}
public class Mainframe2 {
public static Timeline time;
@FXML Button setDarkModeButton;
@FXML
public void options(ActionEvent event){
try{
TestApp.toggleMode();
Stage stage = (Stage) ((Node)event.getSource()).getScene().getWindow();
FXMLLoader loader = new FXMLLoader(getClass().getResource("Mainframe.fxml"));
Parent root = (Parent) loader.load();
Mainframe ctrl = loader.getController();
//setDarkModeButton.getStylesheets().set(0, "style_lightmode.css");
if(TestApp.isItDarkmode){
root.getStylesheets().set(0, Mainframe.class.getResource(TestApp.mainDarkModePath).toString());
}else{
root.getStylesheets().set(0, Mainframe.class.getResource(TestApp.mainLightModePath).toString());
}
}catch (Exception e) {
System.out.println("Error bei der App Klicken von ModeButton. \n error is: "+e);
e.printStackTrace();
}
}
@FXML
public BorderPane primaryparent;
//get and set for elements
@FXML
void initialize() {
//Check Theme
if(TestApp.isItDarkmode){
primaryparent.getStylesheets().clear();
primaryparent.getStylesheets().add(getClass().getResource(TestApp.mainDarkModePath).toString());
}else if(!TestApp.isItDarkmode){
primaryparent.getStylesheets().clear();
primaryparent.getStylesheets().add(getClass().getResource(TestApp.mainLightModePath).toString());
}
}
}
看到那种没人对我的问题感兴趣的。 这不是我想要的,但只要没有人有更好的解决方案:
您可以关闭旧阶段并使用新阶段创建新阶段 Theme-style。
所以在TestApp.class
中必须设置setPrimaryWindow()
方法public
和static
。所以你不能使用 .getClass()
并且 pStage 必须设置为 setPrimaryWindow()
:
public static void setPrimaryWindow(Stage primaryStage){
try{
pStage = primaryStage;
FXMLLoader fxmlLoader = new FXMLLoader(TestApp.class.getResource("Mainframe.fxml"));
Parent root = (Parent) fxmlLoader.load();
Scene scene = new Scene(root);
primaryStage.setTitle("TestApp");
//scene.setMoveControl(titleBar);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e){
e.printStackTrace();
}
}
@Override
public void start(Stage primaryStage) {
setPrimaryStage(primaryStage);
setPrimaryWindow(primaryStage);
}
在选项 maincontroller.class
中,您只需 close()
旧的 window 并调用 setPrimaryWindow()
:
@FXML
public void options(ActionEvent event){
try{
TestApp.toggleMode();
Stage stage = (Stage) ((Node)event.getSource()).getScene().getWindow();
Stage changer = TestApp.getPrimaryStage();
stage.close();
changer.close();
//create new Window on Theme switch
Stage nStage = new Stage();
TestApp.setPrimaryWindow(nStage);
}catch (Exception e) {
System.out.println("Error bei der App Klicken von ModeButton. \n error is: "+e);
e.printStackTrace();
}
}
Css 文件可以在 运行 上更改,无需重新加载或设置新阶段
用 set()
方法替换 observableList
App.java
public class App extends Application {
@Override
public void start(Stage stage) {
Button button = new Button("change");
button.setOnAction(e ->button.getScene().getStylesheets().set(0, "1.css"));
Button button1 = new Button("default");
button1.setOnAction(e ->button.getScene().getStylesheets().set(0, "0.css"));
HBox hBox = new HBox(button,button1);
AnchorPane anchorPane =new AnchorPane(hBox);
anchorPane.getStyleClass().add("pane");
var scene = new Scene(anchorPane, 640, 480);
scene.getStylesheets().add("0.css");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
0.css
.pane{
-fx-background-color:cyan;
}
1.css
.pane{
-fx-background-color:orange;
}