#java #css #javafx #pseudo-class
#java #css #javafx #псевдокласс
Вопрос:
Я хочу отображать разные метки в javafx, и я хочу стилизовать их в зависимости от их текста. Я добавил файл css и установил класс меток. Затем я проверил fxml и обнаружил, что текст сохраняется в атрибуте text.
Я заглянул в обычный css и обнаружил, что вы можете там изменить стиль по атрибутам. Для этого вам нужно использовать [] . Я попробовал это в своем коде, и это не сработало.
Мой код: FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.net.URL?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<HBox xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="controller">
<stylesheets>
<URL value="@../css/loadingScreen.css"/>
</stylesheets>
<Label styleClass="field" text="1" />
<Label styleClass="field" text="2" />
<Label styleClass="field" text="3" />
</HBox>
CSS:
.field {
-fx-text-alignment: center;
-fx-pref-height: 64px;
-fx-min-width: 64px;
-fx-pref-width: 64px;
-fx-min-height: 64px;
-fx-background-color: blue;
}
.field[text="1"]{
-fx-background-color: red;
}
.field[text="2"]{
-fx-background-color: yellow;
}
.field[text="3"]{
-fx-background-color: green;
}
я попробовал то же самое с обычными css и html, и там это сработало.
HTML:
<!DOCTYPE html>
<html>
<head>
<style>
.field[text="1"]{
background-color: red;
}
.field[text="2"]{
background-color: yellow;
}
.field[text="3"]{
background-color: green;
}
</style>
</head>
<body>
<div class="field" text="1" >1</div>
<div class="field" text="2" >2</div>
<div class="field" text="3" >3</div>
</body>
</html>
Что мне нужно сделать, чтобы это работало в fxml?
Комментарии:
1. Не верьте, что JavaFX-CSS поддерживает то, что вы хотите. Почему бы не присвоить каждому
Label
определенный класс стиля на основе текста? Или, если у вас есть только приблизительно три метки, каждая из которых имеет уникальный стиль, вы могли бы вместо этого установить их ID .2. спасибо за ответ, я, вероятно, собираюсь использовать стиль для каждого текстового значения. Первоначальная причина, по которой я хотел задать стиль по атрибуту, заключалась в том, что если бы я изменил текст, это также автоматически изменило бы стиль без необходимости менять класс.
3. Slaw прав, смотрите этот раздел руководства по регрессии CSS, начинающийся со списка: openjfx.io/javadoc/11/javafx.graphics/javafx/scene/doc-files/… Прямо сейчас вы, похоже, не обновляете
Label
s, поэтому я не уверен, почему вы просто не работаете с идентификаторами / классами стилей, чтобы «указатьLabel
s appart» в CSS…
Ответ №1:
Если бы я изменил текст, это также автоматически изменило бы стиль
Вариант 1: управлять стилем по идентификатору
Этого можно добиться, используя пользовательскую метку, которая меняет стиль при изменении текста. Я продемонстрирую это, изменив идентификатор метки. В этом упрощенном примере текст используется в качестве идентификатора :
import javafx.geometry.Pos;
import javafx.scene.control.Label;
public class CustomLabel extends Label{
public CustomLabel() {
setAlignment(Pos.CENTER);
setPrefSize(50, 25);
}
void setTextAndId(String s){
super.setText(s);
/*To keep this demo simple and clear id is changed.
If used, care must be taken to keep id unique.
Using setStyle() or PseudoClass should be preferred
*/
setId(s);
}
}
Пользовательскую метку можно использовать в fxml ( Root.fxml
):
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.StackPane?>
<?import tests.CustomLabel?>
<StackPane fx:id="root" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="tests.Controller">
<children>
<CustomLabel fx:id="cLabel" text="amp;quot;amp;quot;" />
</children>
</StackPane>
Простой css, который изменяет цвет фона на основе id ( Root.css
):
#1{
-fx-background-color: red;
-fx-text-fill: white;
}
#2{
-fx-background-color: yellow;
-fx-text-fill: red;
}
#3{
-fx-background-color: green;
-fx-text-fill: yellow;
}
Тестовый класс:
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class LabelCssTest extends Application {
@Override public void start(Stage stage) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("Root.fxml"));
stage.setScene(new Scene(root));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
И контроллер тестирования :
import javafx.animation.PauseTransition;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.util.Duration;
public class Controller {
@FXML
CustomLabel cLabel;
@FXML Parent root;
private static final int MIN_VALUE = 1, MAX_VALUE = 3;
private int counter = MIN_VALUE;
@FXML
private void initialize() {
root.getStylesheets().add(getClass().getResource("Root.css").toExternalForm());
cLabel.setTextAndId(String.valueOf(counter ));
PauseTransition pause = new PauseTransition(Duration.seconds(2));
pause.setOnFinished(event ->{
cLabel.setTextAndId(String.valueOf(counter ));
if(counter > MAX_VALUE) {
counter = MIN_VALUE;
}
pause.play();
});
pause.play();
}
}
Вариант 2: управляйте стилем, изменяя style-class
Используйте тот же тестовый класс, что и вариант 1.
Root.fxml
:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.StackPane?>
<StackPane fx:id="root" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tests.Controller">
<children>
<Label fx:id="label" alignment="CENTER" contentDisplay="CENTER" prefHeight="20.0" prefWidth="70.0" text="amp;quot; amp;quot;" />
</children>
</StackPane>
Root.css
:
.style1{
-fx-background-color: red;
-fx-text-fill: white;
}
.style2{
-fx-background-color: yellow;
-fx-text-fill: red;
}
.style3{
-fx-background-color: green;
-fx-text-fill: yellow;
}
И контроллер:
import javafx.animation.PauseTransition;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.util.Duration;
public class Controller {
@FXML Label label;
@FXML Parent root;
private static final int MIN_VALUE = 1, MAX_VALUE = 3;
private SimpleIntegerProperty counter = new SimpleIntegerProperty();
@FXML
private void initialize() {
root.getStylesheets().add(getClass().getResource("Root.css").toExternalForm());
counter = new SimpleIntegerProperty();
counter.addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
label.getStyleClass().clear();
label.getStyleClass().add("style" counter.get());
});
label.textProperty().bind(Bindings.createStringBinding(()->String.valueOf(counter.get()), counter));
counter.set(1);
PauseTransition pause = new PauseTransition(Duration.seconds(2));
pause.setOnFinished(event ->{
counter.set(counter.get() >= MAX_VALUE ? MIN_VALUE : counter.get() 1);
pause.play();
});
pause.play();
}
}
Вариант 3: управляйте стилем с помощью PseudoClass
:
Изменения из варианта 2:
Root.css
:
.root:style1 #label{
-fx-background-color: red;
-fx-text-fill: white;
}
.root:style2 #label{
-fx-background-color: yellow;
-fx-text-fill: red;
}
.root:style3 #label{
-fx-background-color: green;
-fx-text-fill: yellow;
}
И контроллер:
import javafx.animation.PauseTransition;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.css.PseudoClass;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.util.Duration;
public class Controller {
@FXML Label label;
@FXML Parent root;
private static final int MAX_VALUE = 3;
private SimpleIntegerProperty counter = new SimpleIntegerProperty(1);
@FXML
private void initialize() {
root.getStylesheets().add(getClass().getResource("Root.css").toExternalForm());
counter = new SimpleIntegerProperty();
counter.addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
updateStates();
});
label.textProperty().bind(Bindings.createStringBinding(()->String.valueOf(counter.get()), counter));
counter.set(1);
PauseTransition pause = new PauseTransition(Duration.seconds(2));
pause.setOnFinished(event ->{
counter.set(counter.get() >= MAX_VALUE ? 1 : counter.get() 1);
pause.play();
});
pause.play();
}
private void updateStates() {
for( int index = 1; index <= MAX_VALUE; index ){
PseudoClass pc = PseudoClass.getPseudoClass("style" String.valueOf(index));
root.pseudoClassStateChanged(pc, index == counter.get() ? true : false);
}
}
}
Комментарии:
1. хм… разве идентификатор не должен быть уникальным во всем документе?
2. спасибо за предложение, я реализовал это, и оно отлично работает. я немного изменил это. это изменяет класс вместо идентификатора, потому что пользовательская метка может иметь тот же текст, что и другие пользовательские метки.
3. Изменение класса стиля или использование псевдокласса, безусловно, лучше. Опубликованный метод был выбран для наглядности и простоты.
4. @kleopatra Вы правы. отсюда комментарий «//возможно, вы захотите реализовать здесь некоторую логику». Эта упрощенная демонстрация не нарушает его, но я улучшил формулировку комментария. Спасибо за ваш комментарий.
Ответ №2:
Если вы просто хотите изменить цвет фона Label
в зависимости от текста, вы также можете просто создать метод, подобный этому, и вызывать его для каждого Label
, когда вам это нужно.
import javafx.scene.control.Label;
import javafx.geometry.Insets;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.paint.Color;
private void backgroundColorTextDependent(Label label) {
if (label.getText().equals("1")) {
label.setBackground(new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY)));
} else if (label.getText().equals("2")) {
label.setBackground(new Background(new BackgroundFill(Color.YELLOW, CornerRadii.EMPTY, Insets.EMPTY)));
} else if (label.getText().equals("3")) {
label.setBackground(new Background(new BackgroundFill(Color.GREEN, CornerRadii.EMPTY, Insets.EMPTY)));
} else {
label.setBackground(new Background(new BackgroundFill(Color.BLUE, CornerRadii.EMPTY, Insets.EMPTY)));
}