JavaFX: динамически окрашенное окно с CSS - PullRequest
1 голос
/ 24 сентября 2019

Я пытаюсь создать объект Pane с JavaFX, который имеет три разных цвета: цвет для фона, цвет для текста и цвет для кнопок.Каждый из этих трех цветов определяется динамически во время выполнения на основе значений пользовательского объекта, переданного в метод.Я выяснил, как довольно легко реализовать это поведение непосредственно в моем коде, и кажется, что я мог бы использовать контроллер и метод инициализации, чтобы настроить его с помощью FXML.Но мне интересно, было бы возможно или целесообразно настроить что-то подобное с помощью CSS.

Насколько я могу судить, CSS на самом деле не использует переменные из кода, просто жестко закодированные значениябыть установленным заранее.Учитывая огромное количество потенциальных комбинаций, не похоже, что было бы целесообразно составлять разные листы для каждой из них.Тем не менее, я слышал, что использование CSS является современной практикой, поэтому мне интересно, можно ли использовать один лист для создания нескольких различных типов панелей или что каждая возможная панель должна быть уникально определена со своим собственным листом, дажесо всем остальным, что панель идентична.

Ответы [ 3 ]

2 голосов
/ 25 сентября 2019

Тебе повезло, так как разница только в цветах.Вы можете использовать цвета поиска для этого puropse: используйте my-color-name: <value>; правила для самого узла или для одного из предков.Это позволяет вам указывать эти значения inline 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 stlyesheet

.box {
    -fx-background-color: -my-background;
}

Таким способом можно указать несколько цветовНапример,

root.setStyle("-my-color: red; -some-other-color: brown; -color-3: yellow;");
2 голосов
/ 25 сентября 2019

Я согласен с тем, что @Sedrick упомянул в комментариях.

Если вы хотите изменить только цвета, не изменяя остальную часть 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);
    }
}

base.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;
}

type1.css:

.root{
   -fx-window-border: red;
   -fx-window-color: yellow;
   -fx-window-text: brown;
   -fx-button-color: pink;
}

type2.css:

.root{
   -fx-window-border: green;
   -fx-window-color: lightblue;
   -fx-window-text: white;
   -fx-button-color: grey;
}

type3.css:

.root{
   -fx-window-border: brown;
   -fx-window-color: lightgreen;
   -fx-window-text: blue;
   -fx-button-color: yellow;
}

enter image description here

1 голос
/ 26 сентября 2019

Вот несколько конкретных примеров динамической установки цветов темы для заданных компонентов с использованием:

  1. предложения Фабиана о поиске цветов.
  2. программно созданные таблицы стилей css, записанные во временные файлы.

Образец с использованием искомых цветов

Что образец делает, устанавливает некоторыестандартного поиска цветов, которые были найдены в modena.css для стилизации трех вещей, которые вы хотите стилизовать:

  1. Цвет фона (-fx-background-color).Это стандартный фон, используемый в классах, производных от класса Pane.
  2. Цвет текста (-fx-text-background-color).Да, смущенно названный, я знаю, но это, кажется, то, что это, по любой причине.
  3. Цвет для кнопок (-fx-base).

sample colors

В примере приложения пользователь может выбирать цвета динамически, используя элементы управления JavaFX ColorPicker, чтобы изменять цвета элементов, отображаемых впанель предварительного просмотра.

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);
    }
}

Образец с использованием динамических таблиц стилей

Таким образом, цветовое решение поиска очень мощное, потому что вы можете динамически стилизовать цвет всех элементов в сцене.Тем не менее, CSS в целом гораздо мощнее, чем просто настройка цвета.Если для вашей динамической стилизации требуется нечто большее, чем просто настройка цвета, или вам нужны очень конкретные правила для стилизации определенного элемента в сцене, тогда вам понадобится ваша собственная таблица стилей.

Свойство stylesheets узлов и сцен представляет собой динамический наблюдаемый список.Так что, если вы измените таблицы стилей, вы измените стиль узла или сцены.На каждую таблицу стилей ссылается URL.Таким образом, чтобы динамически создать таблицу стилей, все, что вам нужно сделать, это создать содержимое таблицы стилей в коде и записать его во временный файл, получить ссылку URL на временный файл, а затем установить его в качестве таблицы стилей для того, что вы хотитеto style.

Чтобы привести пример этого подхода, просто возьмите код из предыдущего примера, используя искомые цвета, и замените метод setThemeColors на метод ниже.После этого будет выполнено динамическое оформление панели предварительного просмотра с использованием динамически создаваемого файла CSS, а не искомых цветов.

Примечание. При создании динамической таблицы стилей я пытался использовать селектор .root для определения стилей(аналогично ответу Саи), но по какой-то причине он не работал (возможно, он не работает с моей версией 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 для искомых цветов ,Это мощный метод для достижения желаемого, и концепция (насколько я знаю) свойственна обработке CSS JavaFX и не существует со стандартным CSS на основе HTML.

С поисковыми цветамивы можете ссылаться на любое другое свойство цвета, установленное на текущем узле или на любом из его родительских элементов.Это очень мощная функция, так как она позволяет определять общую палитру цветов на сцене, а затем использовать ее во всем приложении.Если вы хотите изменить один из этих цветов палитры, вы можете сделать это на любом уровне дерева сцены, и это повлияет на этот узел и все его потомки.Поисковые цвета не ищутся, пока они не применены, поэтому они активны и реагируют на любые изменения стиля, которые могут произойти, такие как замена цвета палитры во время выполнения на свойство "style" на узле.

Если вы будете искать внутри jar-файлов, которые поставляются с JavaFX SDK, который вы используете, вы найдете файл с именем modena.css.Этот файл предопределяет многие искомые цвета, которые вы можете переопределить, чтобы упростить тему вашего приложения.Они нигде не документированы, вам нужно взглянуть на файл modena.css, чтобы увидеть, какие они есть (самые полезные находятся в разделе .root файла).Самый важный цвет - -fx-base, который задает основной цвет для всей системы управления JavaFX.

Просматриваемые цвета часто сочетаются с некоторыми другими концепциями JavaFX CSS, такими как деривация и рендеринг, для создания согласованных тем, которые по-прежнему читаются при изменении базового цвета поиска.Это позволяет изменить базовый цвет, например, с белого на черный, и текст, отображаемый в элементах управления на основе базового цвета, автоматически изменится с обратно на белый, чтобы его можно было читать.

Что такое-fx-base?

fx-base - это основной цвет для всех элементов управления, поэтому его установка изменит цвет всех элементов управления в сцене, что, вероятно, вам и нужно, но, возможно, нет.

Если вы хотите изменить только кнопки, а не все в сцене, просто установите цвет -fx-base непосредственно на каждую из кнопок, а не на включающую панель.Один из хитростейших способов сделать это состоит в том, что вы можете определить свой собственный стиль CSS для кнопки и установить для него значение -fx-base: my-custom-color, а затем динамически установить значение my-custom-color в вашей программе, как показано в ответе Фабиана.

Обратите внимание, что установка основного цвета предпочтительнее, чем попытка установить фактический цвет кнопки.Потому что, сама кнопка, когда вы смотрите на нее внимательно, включает в себя различные градиенты, и затенение происходит от основного цвета, так что на самом деле она состоит из нескольких цветов при ее рендеринге, а не только из одного цвета.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...