Мне нужна кнопка, которая позволяет мне переключаться между темным/светлым режимом. Но у меня проблема Если я переключаюсь, активные окна не меняют свой стиль.
Первый из фрагментов кода, чтобы легко воссоздать проблему. Для стартового класса проектов Maven:
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);
}
}
}
Основной класс окна:
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);
}
}
Класс основного контроллера:
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());
}
}
}
В этих фрагментах кода происходит много странных вещей. Вы выходите из платформы, а затем пытаетесь что-то делать с JavaFX -> вы не можете этого сделать. Вы закрываете этап и пытаетесь запустить новое приложение -> вы не можете этого сделать. Вы пытаетесь инициировать стиль на сцене после того, как она видна -> вы не можете этого сделать.
См.: некоторая информация о применении тем через css в JavaFX.
Кроме того, возможно, все, что вам нужно сделать, это установить -fx-base в корне цвета, который вы хотите, попробуйте, это может быть не совсем то, что вы хотите, но это намного проще.
Вы также назвали класс Application (app.Application). Не делайте этого, это очень запутывает. JavaFX уже имеет Класс приложения. Прочтите Javadoc, чтобы понять, как работает жизненный цикл и каковы правила его использования.
@jewelsea Я переименовал свои классы в stackoverlow, чтобы уменьшить их. Поэтому я сделал небольшие фрагменты всех файлов, которые необходимы для воссоздания проблемы так, как мне нужно. Надеюсь, теперь вы сможете мне помочь :)
пожалуйста, прочитайте указанную страницу справки и действуйте соответственно - обратите внимание на М .. это выглядит как слишком много кода (alwaysontop не имеет отношения к стилю, и большинство правил стиля не нужны) и по-прежнему отсутствует трассировка стека .. не имеет отношения: не использовать статическую область видимости..
.. или вы не получаете никаких ошибок? Если да, то это реально или просто потому, что вы их проглотили в ранлейтере?
Нет, это не создает никакой ошибки. Я удалил все, что пытался заставить это работать... Я понятия не имею, как это сделать.
Хорошо добавил одну из вещей, которые я пробовал: initialize() . Ошибки не выдает, но и не действует. Итак, но теперь я действительно надеюсь, что вы, ребята, сможете мне помочь...
Я немного запутался с вашим Mainframe2 классом. В методе options вы загружаете файл FXML и добавляете таблицу стилей в корень Node. Но вы никогда не добавляете этот Node в граф сцены, поэтому он не отображается пользователю. Таким образом, никаких визуальных эффектов. Если вы хотите изменить стиль всех узлов, отображаемых в данный момент в сцене, либо измените список таблиц стилей Scene, либо rootScene.
БЛАГОДАРЮ ВАС!!! Я забыл changer.setScene(scene). Человек это была борьба ....






Видно, что вроде никого не интересует мой вопрос. Это не то, что я хотел, но пока ни у кого нет лучшего решения:
Вы можете закрыть старую сцену и создать новую с новым стилем темы.
Таким образом, в 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() старое окно и вызываете 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;
}
Хм, когда я добавляю это в свой код, я получаю эту ошибку: java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0 Вы знаете, как это сделать с файлами fxml?
В моем Main Controller я использовал это на ActionEvent: setDarkModeButton.getScene().getStylesheets().set(0, ".\\style_lightmode.css");
@Muddyblackk В списке должен быть хотя бы один элемент, чтобы set(0, element) работал. Вы можете видеть, что scene.getStylesheets().add("0.css") вызывается в приведенном выше примере перед любым из вызовов set(...). Кроме того, когда вы добавляете таблицу стилей без схемы URL, предполагается, что путь является именем ресурса; поиск ресурсов не поддерживает . и ...
Да, я добавил их в initialize(), но не понимаю, почему я добавил туда ````.getScene()``` ^^ Спасибо, это сработало! После того, как я правильно реализовал его в своем реальном приложении, я отмечу его как ответ :)
Хорошо, это работает для самого нового открытого окна, но как я могу применить его к другому активному окну? Другие сказали, что у меня бесполезный код, так что это больше не мой вопрос... ctrl.primaryparent.getStylesheets().set(0, getClass().getResource(FirstController.mainDarkModePath).toString()); Поэтому я хочу использовать его в другом контроллере. ctrl загружает другой контроллер, как в моем вопросе выше.
Этот код выше ничего не делает. Даже без ошибок
вам нужно знать, как передавать значения между этапами
Я публикую это просто для того, чтобы указать: «Вы можете закрыть старую сцену и создать новую с новым стилем темы». нет необходимости перезагружать или загружать новый этап, чтобы изменить его стиль
Да ctrl.primaryparent — это значение из другого этапа, но оно не обновляет стиль.
Хорошо, @slaw указал на мою проблему :) Спасибо, ребята, за ваше терпение. Я действительно боролся в этот раз...
если вы освоите передачу значений между контроллерами, вы сможете решить многие подобные задачи