- Учебник по JavaFX: CSS-стилизация
- Разделение визуальных элементов
- CSS
- Selectors (Селекторы)
- Class (Класс)
- Built-in classes (Встроенные классы)
- Custom classes (Пользовательские классы)
- ID
- Properties (Свойства)
- Псевдоклассы
- Пользовательские псевдоклассы
- Таблица стилей по умолчанию
- Scene stylesheet (Таблица стилей сцены)
- Parent stylesheet (Родительская таблица стилей)
- Inline styles (Встроенные стили)
- Почему нужно их избегать
- Stylesheet priorities (Приоритеты таблиц стилей)
- Дополнительное чтение
Учебник по JavaFX: CSS-стилизация
Это пост о том как стилизовать компоненты JavaFX, используя старый добрый CSS.
Все посты в серии о JavaFX:
Разделение визуальных элементов
В предыдущей статье о FXML мы узнали, как JavaFX обеспечивает четкое разделение задач путем разделения кода пользовательского интерфейса на две части. Компоненты и их свойства объявлены в файле FXML, а логика взаимодействия четко выделена в контроллер.
Кроме этого, есть и третья часть, язык FXML, управляющий только компонентами вашего приложения, их свойствами и тем, как они вложены друг в друга. Он не определяет визуальные элементы компонента, а именно: шрифты, цвета, фоны, отступы. В целом вы можете достичь этого в FXML, но не стоит этого делать. Вместо этого визуальные элементы должны быть четко определены в таблицах стилей CSS.
Таким образом, ваш дизайн становится независимым и может быть легко заменен или изменен без ущерба для остальной части приложения. Вы можете даже просто реализовать несколько тем, которые можно переключать по желанию пользователя.
CSS
Вы, вероятно, знакомы с CSS (Cascading Style Sheets — Каскадные таблицы стилей), используемыми для стилизации HTML-страниц в Интернете. Похожий подход реализован в JavaFX, несмотря на то, что JavaFX использует набор своих собственных пользовательских свойств.
Давайте рассмотрим пример:
Здесь использованы две основные концепции. Первая — это селектор — .button. Он определяет, к каким компонентам должен применяться стиль. В этом примере стиль применяется для всех кнопок.
Вторая часть — это фактические свойства стиля, которые будут применены ко всем компонентам, которые соответствуют нашему селектору. Свойства — это все, что расположено внутри фигурных скобок.
Каждое свойство имеет определенное значение. В нашем примере есть свойство -fx-font-size, которое определяет, насколько большим будет текст. В примере указано значение 15px, но это значение может быть любым другим.
Подводя итог — мы создали правило, которое гласит — все кнопки везде должны иметь текст размером 15 пикселей.
Selectors (Селекторы)
Теперь давайте рассмотрим подробнее, как работают селекторы в JavaFX. Это происходит почти так же, как в обычном CSS.
Class (Класс)
Класс в CSS представляет несколько похожих элементов. Например, кнопки или флажки. Селектор, который должен применяться ко всем элементам одного класса, начинается с точки «.», сопровождаемый непосредственно именем класса. Соглашение об именовании классов состоит в том, чтобы разделять отдельные слова с помощью символа «-«. Следующий селектор применяется ко всем элементам с классом label.
Built-in classes (Встроенные классы)
Хорошая новость заключается в том, что все встроенные компоненты JavaFX (такие как Label или Button) уже имеют предопределенный класс. Если вы хотите настроить стиль всех меток в своем приложении, вам не нужно добавлять какие-либо пользовательские классы для каждой из ваших меток. Каждая метка по-умолчанию имеет класс label.
Легко определить имя класса из компонента:
- Возьмите имя Java класса компонента — например. Label
- Сделайте имя строчным
- Если он состоит из нескольких слов, разделите их с помощью символа «-«
Custom classes (Пользовательские классы)
Если встроенных классов недостаточно, вы можете добавить свои собственные классы к своим компонентам. Вы можете использовать несколько классов, разделенных запятой:
Label label = new Label("I am a simple label"); label.getStyleClass().addAll("my-label", "other-class");
Добавление классов таким способом не удаляет класс компонента по умолчанию (в данном случае label).
Существует один специальный класс, называемый root. Он является корневым компонентом вашей сцены. Вы можете использовать его, чтобы стилизовать все внутри вашей сцены (например, установить глобальный шрифт). Это похоже на использование селектора тегов body в HTML.
ID
Другим способом выбора компонентов в CSS является использование идентификатора компонента (ID). Он является уникальным идентификатором компонента. В отличие от классов, которые могут быть назначены нескольким компонентам, идентификатор должен быть уникальным в сцене.
В то время как, для указания класса используют символ «.» перед именем в их селекторах, идентификаторы помечаются символом «#».
В FXML вы можете использовать fx:id для установки CSS-идентификатора компонента.
Однако есть одна оговорка. Этот же идентификатор используется для ссылки на объект компонента, объявленный в вашем контроллере с тем же именем. Так как идентификатор и имя поля в контроллере должны совпадать, fx:id должен учитывать ограничение именования Java для имен полей. Хотя соглашение об именах CSS определяет отдельные слова, разделенные символом «-«, это недопустимый символ для имен полей Java. Поэтому для fx:id с несколькими словами вам нужно использовать другое соглашение об именах, такое как CamelCase, или использовать подчеркивание.
В Java вы можете просто вызвать метод setId() вашего компонента.
Label label = new Label("I am a simple label"); label.setId("foo");
Properties (Свойства)
Хотя CSS, используемый в JavaFX, очень похож на оригинальный веб-CSS, есть одно большое отличие. Имена свойств различны, и есть много новых свойств, специфичных для JavaFX. Они имеют префикс -fx-.
- -fx-background-color: Цвет фона
- -fx-text-fill: Цвет текста
- -fx-font-size: Размер текста
Псевдоклассы
В дополнение к обычным классам, которые отмечают определенные компоненты, существуют так называемые псевдоклассы, которые обозначают состояние компонента. Это может быть, например, класс для маркировки того, что компонент имеет фокус или на нем находится курсор мыши.
Есть множество встроенных псевдоклассов. Давайте посмотрим на кнопку. Существует несколько псевдоклассов, которые вы можете использовать, например:
- hover: мышь над кнопкой
- focused: кнопка имеет фокус
- disabled: кнопка отключена
- pressed: кнопка нажата
В отличие от CSS, который имеет только базовые псевдоклассы для состояний, таких как focus и hover, JavaFX имеет специфичные для компонента псевдоклассы, которые относятся к различным состояниям или свойствам компонентов.
- Полосы прокрутки (Scrollbars) имеют псевдоклассы horizontal и vertical
- Элементы (Cells) имеют псевдоклассы odd и even
- TitledPane имеет псевдоклассы expanded и collapsed
Пользовательские псевдоклассы
В дополнение ко встроенным псевдоклассам, вы можете определять и использовать свои собственные псевдоклассы.
Давайте создадим нашу собственную метку (наследуя от класса Label). У него будет новое логическое свойство, называемое shiny. В таком случае, мы хотим, чтобы у нашей метки был псевдокласс shiny.
Поскольку у метки есть псевдокласс shiny, мы можем установить фон метки gold:
Теперь создадим сам класс.
public class ShinyLabel extends Label < private BooleanProperty shiny; public ShinyLabel() < getStyleClass().add("shiny-label"); shiny = new SimpleBooleanProperty(false); shiny.addListener(e ->< pseudoClassStateChanged(PseudoClass.getPseudoClass("shiny"), shiny.get()); >); > public boolean isShiny() < return shiny.get(); >public void setShiny(boolean shiny) < this.shiny.set(shiny); >>
Здесь есть несколько важных частей:
- У нас есть логическое свойство BooleanProperty вместо обычного boolean. Это означает, что объект shiny является observable (наблюдаемым), и мы можем отслеживать (слушать) изменения в его значении.
- Мы регистрируем listener (слушатель), который будет вызываться каждый раз, когда значение объекта shiny изменяется с использованием shiny.addListener().
- Когда значение shiny изменяется, мы добавляем/удаляем псевдокласс shiny в зависимости от текущего значения pseudoClassStateChanged(PseudoClass.getPseudoClass(«shiny»), shiny.get()).
- Мы добавляем пользовательский класс для всех меток shiny-label, вместо того чтобы иметь только класс label, унаследованный от родителя. Таким образом, мы можем выбирать только метки shiny.
Таблица стилей по умолчанию
Даже если вы сами не предоставляете никаких стилей, каждое приложение JavaFX уже имеет некоторые визуальные стили. Существует таблица стилей по умолчанию, которая применяется к каждому приложению. Она называется modena (начиная с JavaFX 8, ранее она называлась caspian).
Эту таблицу стилей можно найти в файле:
jfxrt.jar\com\sun\javafx\scene\control\skin\modena\modena.css
Или вы можете найти файл здесь. В том же каталоге есть множество изображений, используемых таблицей стилей.
Эта таблица стилей предоставляет стили по умолчанию, но имеет самый низкий приоритет по сравнению с другими типами таблиц стилей, поэтому вы можете легко ее переопределить.
Scene stylesheet (Таблица стилей сцены)
В дополнение к таблице стилей по умолчанию, упомянутой выше, вы, конечно, можете предоставить свою собственную. Самый высокий уровень, на котором вы можете применить стилизацию — это вся сцена. Вы можете реализовать это в вашем FXML:
String stylesheet = getClass().getResource("/styles.css").toExternalForm(); scene.getStylesheets().add(stylesheet);
Обратите внимание на вызов toExternalForm(). Scene ожидает получить содержимое таблицы стилей в виде строки, а не файла, поэтому нам нужно предоставить содержимое нашей таблицы стилей в виде строки.
Parent stylesheet (Родительская таблица стилей)
В дополнение к таблице стилей для всей сцены, иногда бывает полезно иметь стили на уровне макета. То есть — для отдельного контейнера, такого как VBox, HBox или GridPane. Общим родителем всех макетов является родительский класс, который определяет методы для обработки таблиц стилей на уровне макета. Эти стили применяются только для компонентов в данном макете, а не для всей сцены. Стиль на уровне макета имеет приоритет над стилем на уровне сцены.
В Java вам нужно загрузить содержимое таблицы стилей самостоятельно, так же как и ранее для сцены:
HBox box = new HBox(); String stylesheet = getClass().getResource("/styles.css").toExternalForm(); box.getStylesheets().add(stylesheet);
Inline styles (Встроенные стили)
Пока что мы рассмотрели только случаи назначения внешней таблицы стилей для всей сцены или макета. Но можно задать индивидуальные свойства стиля на уровне компонента.
Здесь вам не нужно беспокоиться о селекторе, так как все свойства установлены для определенного компонента.
Вы можете указать несколько свойств, разделенных точкой с запятой:
В Java вы можете использовать метод setStyle():
Label label = new Label("I'm feeling blue."); label.setStyle("-fx-background-color: blue; -fx-text-fill: white");
Стили на уровне компонента имеют приоритет как над стилями сцены так и над родительскими стилями на уровне макета.
Почему нужно их избегать
Стилизация на уровне компонентов может быть удобной, но это быстрое и «грязное» решение. Вы отказываетесь от основного преимущества CSS, которое заключается в отделении стилей от компонентов. Теперь вы жестко привязываете свои визуальные элементы непосредственно к компонентам. Вы больше не сможете легко переключать свои таблицы стилей, когда это необходимо, вы не можете менять темы.
Более того, у вас больше нет единого центрального места, где определяется ваш стиль. Когда вам нужно что-то изменить в наборе похожих компонентов, вам нужно изменить каждый из компонентов по отдельности, а не редактировать только одно место во внешней таблице стилей. Поэтому следует избегать встроенных стилей компонентов.
Stylesheet priorities (Приоритеты таблиц стилей)
Вы можете обеспечить стилизацию на нескольких уровнях — сцена, родительский, встроенные стили, и также есть таблица стилей модены по умолчанию. Если вы изменяете одно и то же свойство одного и того же компонента на нескольких уровнях, JavaFX имеет настройку приоритета, которая определяет, какие стили следует использовать. Список приоритетов — от высшего к низшему:
- Inline styles (Встроенные стили)
- Parent styles (Родительские стили)
- Scene styles (Стили сцены)
- Default styles (Стили по умолчанию)
Дополнительное чтение
В JavaFX имеется множество свойств CSS, и их описание выходит за рамки этого поста, подробный список см. в официальном справочном руководстве по CSS для JavaFX.