javafx change css at runtime

23,906

Solution 1

It should have the effect. Try this full demo code:

public class CssThemeDemo extends Application {

    private String theme1Url = getClass().getResource("theme1.css").toExternalForm();
    private String theme2Url = getClass().getResource("theme2.css").toExternalForm();

    @Override
    public void start(Stage primaryStage) {
        StackPane root = new StackPane();
        final Scene scene = new Scene(root, 300, 250);
        System.out.println("scene stylesheets: " + scene.getStylesheets());
        scene.getStylesheets().add(theme1Url);
        System.out.println("scene stylesheets: " + scene.getStylesheets());

        final Button btn = new Button("Load Theme 1");
        btn.getStyleClass().add("buttonStyle");
        btn.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                scene.getStylesheets().remove(theme2Url);
                System.out.println("scene stylesheets on button 1 click: " + scene.getStylesheets());
                if(!scene.getStylesheets().contains(theme1Url)) scene.getStylesheets().add(theme1Url);
                System.out.println("scene stylesheets on button 1 click: " + scene.getStylesheets());
            }
        });

        final Button btn2 = new Button("Load Theme 2");
        btn2.getStyleClass().add("buttonStyle");
        btn2.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                scene.getStylesheets().remove(theme1Url);
                System.out.println("scene stylesheets on button 2 click: " + scene.getStylesheets());
                if(!scene.getStylesheets().contains(theme2Url)) scene.getStylesheets().add(theme2Url);
                System.out.println("scene stylesheets on button 2 click: " + scene.getStylesheets());
            }
        });

        ComboBox<String> comboBox = new ComboBox<String>(FXCollections.observableArrayList("Just", "another", "control"));
        root.getChildren().add(VBoxBuilder.create().spacing(10).children(btn, btn2, comboBox).build());

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

theme1 css:

.root{
    -fx-font-size: 14pt;
    -fx-font-family: "Tahoma";
    -fx-base: #DFB951;
    -fx-background: #A78732;
    -fx-focus-color: #B6A678;
}

.buttonStyle {
    -fx-text-fill: #006464;
    -fx-background-color: #DFB951;
    -fx-border-radius: 20;
    -fx-background-radius: 20;
    -fx-padding: 5;
}

theme2 css:

.root{
    -fx-font-size: 16pt;
    -fx-font-family: "Courier New";
    -fx-base: rgb(132, 145, 47);
    -fx-background: rgb(225, 228, 203);
}

.buttonStyle {
    -fx-text-fill: red;
    -fx-background-color: lightcyan;
    -fx-border-color: green;
    -fx-border-radius: 5;
    -fx-padding: 3 6 6 6;
}

Note the same named CSS selectors in both theme1 and theme2 css files.

Solution 2

You can also try this piece of code(simple and truly illustrative) :

  • A Container for it: I chose BorderPane.
  • Add a main Scene for your application.
  • A Menu Bar with a set of items depending on the look of your application.
  • And item on the Menu bar.
BorderPane rootPane = new BorderPane();
Parent content = FXMLLoader.load(getClass().getResource("sample.fxml"));
rootPane.setCenter(content);
Scene scene = new Scene(root, 650, 550, Color.WHITE);
// Menu bar
MenuBar menuBar = new MenuBar();

// file menu
Menu fileMenu = new Menu("_File");
MenuItem exitItem = new MenuItem("Exit");
exitItem.setAccelerator(new KeyCodeCombination(KeyCode.X, KeyCombination.SHORTCUT_DOWN));
exitItem.setOnAction(ae -> Platform.exit());

fileMenu.getItems().add(exitItem);
menuBar.getMenus().add(fileMenu);

// Look and feel menu
Menu themeMenu = new Menu("_Theme");
themeMenu.setMnemonicParsing(true);
menuBar.getMenus().add(themeMenu);
rootPane.setTop(menuBar);


MenuItem theme1 = new MenuItem("Theme 1");
theme1.setOnAction(ae -> {
            scene.getStylesheets().clear();
            setUserAgentStylesheet(null);
            scene.getStylesheets()
                    .add(getClass()
                            .getResource("theme1.css")
                            .toExternalForm());
});

MenuItem theme2 = new MenuItem("Theme 2");
        theme2.setOnAction(ae -> {
            scene.getStylesheets().clear();
            setUserAgentStylesheet(null);
            scene.getStylesheets()
                    .add(getClass()
                            .getResource("theme2.css")
                            .toExternalForm());
});


themeMenu.getItems()
                .addAll(theme1,
                        theme2);

primaryStage.setScene(scene);
primaryStage.show();

Supposed that you have your two CSS files in the folder of the class where you will call this code with the corresponding name theme1.css and theme2.css.

Now you can switch between two themes while your application is running.

Share:
23,906
Rob
Author by

Rob

Updated on August 02, 2022

Comments

  • Rob
    Rob almost 2 years

    Is it possible to change css for a JavaFX application while it is running?

    The effect I am looking for is changing skins or themes at the click of a button.

    The UI is in an FXML file if that makes any difference.

    I have tried

    Scene.getStylesheets()
      .add(getClass().getResource(skinFileName).toExternalForm()); 
    

    which has no effect.

    thanks