JavaFx有条件地改变标签文本颜色
JavaFx changing Label text color conditionally
我正在尝试有条件地更改标签的颜色和文本。
文本更改有效,但颜色更改无效。
我收到一个错误:
The method setFill(Color) is undefined for the type When.StringConditionBuilder
代码片段:
assessmentLabel.textProperty().bind(
new When(alcoholPercentageField.textProperty().greaterThanOrEqualTo("5"))
.then("Be careful").setFill(Color.RED)
.otherwise("Smart choice").setFill(Color.GREEN)
);
有什么办法吗?
使用更改侦听器,而不是绑定
您正在尝试使用单个绑定来更改多个目标属性。你不能那样做。
您可以创建多个绑定来更改多个属性,但我不建议这样做。
相反,在您希望监听的 属性 上放置一个更改监听器(在本例中为 alcoholPercentageField.textProperty()),并在监听器的代码块中检查新监听器的状态值并更新所有相关属性(在本例中为评估标签文本和填充)。
一边
数字值的文本比较不是一个好主意,即使在某些 single-digit 情况下可能有效。相反,您应该将文本转换为数字以将其与另一个数字进行比较。
使用 CSS 和样式 classes 而不是直接设置填充。
- 对于某些应用程序来说,这可能更复杂且不必要,因此请在 case-by-case 的基础上对其进行评估。
使用绑定时,请始终检查是否可以使用绑定中的辅助函数 class,例如 Bindings.when().
- 使用绑定 API 通常比使用
When
. 等构造直接创建绑定表达式更好、更流畅且经过充分测试
简单的解决方案
- 演示如何使用更改侦听器来更改多个属性。
- 标签的文本和填充。
- 不使用文本格式化程序或样式 classes.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class SimpleChangeReaction extends Application {
@Override
public void start(Stage stage) {
final TextField alcoholPercentageField = new TextField();
final Label assessmentLabel = new Label();
alcoholPercentageField.textProperty().addListener((observable, oldValue, newValue) -> {
try {
int intValue = Integer.parseInt(newValue);
if (intValue > 5) {
assessmentLabel.setText("Be careful");
assessmentLabel.setTextFill(Color.RED);
} else {
assessmentLabel.setText("Smart choice");
assessmentLabel.setTextFill(Color.GREEN);
}
} catch (NumberFormatException e) {
assessmentLabel.setText("Invalid");
assessmentLabel.setTextFill(Color.ORANGE);
}
});
VBox layout = new VBox(10,
alcoholPercentageField,
assessmentLabel
);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
更复杂的解决方案
- 演示如何使用更改侦听器来更改多个属性。
- 使用文本格式化程序和样式 classes.
- 这个更复杂的解决方案不需要演示更改侦听器,它只是用来显示切换逻辑的替代方法,UI 基于用户输入。
- 复杂解的输出与简单解相同。
- 复杂解决方案的行为不同于简单解决方案,因为它不允许出现无效输入。
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import javafx.util.converter.IntegerStringConverter;
import java.util.Arrays;
import java.util.function.UnaryOperator;
public class ChangeReaction extends Application {
@Override
public void start(Stage stage) {
final Style style = new Style();
final TextField alcoholPercentageField = new TextField();
final Label assessmentLabel = new Label();
PositiveIntTextFormatterFactory formatterFactory = new PositiveIntTextFormatterFactory();
TextFormatter<Integer> positiveIntFormatter = formatterFactory.createPositiveIntTextFormatter(0);
alcoholPercentageField.setTextFormatter(positiveIntFormatter);
positiveIntFormatter.valueProperty().addListener((observable, oldValue, newValue) -> {
if (newValue > 5) {
assessmentLabel.setText("Be careful");
style.getStatusStyleClassChooser().chooseStyleClass(
assessmentLabel,
Style.StatusStyleClassChoices.warning
);
} else {
assessmentLabel.setText("Smart choice");
style.getStatusStyleClassChooser().chooseStyleClass(
assessmentLabel,
Style.StatusStyleClassChoices.ok
);
}
});
VBox layout = new VBox(10,
alcoholPercentageField,
assessmentLabel
);
layout.setPadding(new Insets(10));
Scene scene = new Scene(layout);
scene.getStylesheets().add(Style.CSS);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
final class Style {
public static final String CSS = """
data:text/css,
.label.ok {
-fx-text-fill: green;
}
.label.warning {
-fx-text-fill: red;
}
""";
public enum StatusStyleClassChoices {
ok, warning;
};
private final StyleClassChooser<StatusStyleClassChoices> statusStyleClassChooser = new StyleClassChooser<>(StatusStyleClassChoices.class);
public StyleClassChooser<StatusStyleClassChoices> getStatusStyleClassChooser() {
return statusStyleClassChooser;
}
}
final class PositiveIntTextFormatterFactory {
private static final String POSITIVE_INT_PATTERN = "([1-9][0-9]*)?";
private final UnaryOperator<TextFormatter.Change> positiveIntFilter = change -> {
String newText = change.getControlNewText();
if (newText.matches(POSITIVE_INT_PATTERN) && newText.length() < 4) {
return change; // allow change
}
return null; // disallow change
};
private final StringConverter<Integer> positiveIntConverter = new IntegerStringConverter() {
@Override
public Integer fromString(String s) {
if (s.isEmpty()) return 0 ;
return super.fromString(s);
}
};
public TextFormatter<Integer> createPositiveIntTextFormatter(int defaultValue) {
return new TextFormatter<>(
positiveIntConverter,
defaultValue,
positiveIntFilter
);
}
}
final class StyleClassChooser<T extends Enum<T>> {
private final String[] choices;
public StyleClassChooser(Class<T> styleclassEnum) {
choices = Arrays.stream(styleclassEnum.getEnumConstants())
.map(Enum::toString)
.toArray(String[]::new);
}
public void chooseStyleClass(Node node, T styleClass) {
node.getStyleClass().removeAll(choices);
node.getStyleClass().add(styleClass.toString());
}
}
我正在尝试有条件地更改标签的颜色和文本。
文本更改有效,但颜色更改无效。
我收到一个错误:
The method setFill(Color) is undefined for the type When.StringConditionBuilder
代码片段:
assessmentLabel.textProperty().bind(
new When(alcoholPercentageField.textProperty().greaterThanOrEqualTo("5"))
.then("Be careful").setFill(Color.RED)
.otherwise("Smart choice").setFill(Color.GREEN)
);
有什么办法吗?
使用更改侦听器,而不是绑定
您正在尝试使用单个绑定来更改多个目标属性。你不能那样做。
您可以创建多个绑定来更改多个属性,但我不建议这样做。
相反,在您希望监听的 属性 上放置一个更改监听器(在本例中为 alcoholPercentageField.textProperty()),并在监听器的代码块中检查新监听器的状态值并更新所有相关属性(在本例中为评估标签文本和填充)。
一边
数字值的文本比较不是一个好主意,即使在某些 single-digit 情况下可能有效。相反,您应该将文本转换为数字以将其与另一个数字进行比较。
使用 CSS 和样式 classes 而不是直接设置填充。
- 对于某些应用程序来说,这可能更复杂且不必要,因此请在 case-by-case 的基础上对其进行评估。
使用绑定时,请始终检查是否可以使用绑定中的辅助函数 class,例如 Bindings.when().
- 使用绑定 API 通常比使用
When
. 等构造直接创建绑定表达式更好、更流畅且经过充分测试
- 使用绑定 API 通常比使用
简单的解决方案
- 演示如何使用更改侦听器来更改多个属性。
- 标签的文本和填充。
- 不使用文本格式化程序或样式 classes.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class SimpleChangeReaction extends Application {
@Override
public void start(Stage stage) {
final TextField alcoholPercentageField = new TextField();
final Label assessmentLabel = new Label();
alcoholPercentageField.textProperty().addListener((observable, oldValue, newValue) -> {
try {
int intValue = Integer.parseInt(newValue);
if (intValue > 5) {
assessmentLabel.setText("Be careful");
assessmentLabel.setTextFill(Color.RED);
} else {
assessmentLabel.setText("Smart choice");
assessmentLabel.setTextFill(Color.GREEN);
}
} catch (NumberFormatException e) {
assessmentLabel.setText("Invalid");
assessmentLabel.setTextFill(Color.ORANGE);
}
});
VBox layout = new VBox(10,
alcoholPercentageField,
assessmentLabel
);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
更复杂的解决方案
- 演示如何使用更改侦听器来更改多个属性。
- 使用文本格式化程序和样式 classes.
- 这个更复杂的解决方案不需要演示更改侦听器,它只是用来显示切换逻辑的替代方法,UI 基于用户输入。
- 复杂解的输出与简单解相同。
- 复杂解决方案的行为不同于简单解决方案,因为它不允许出现无效输入。
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import javafx.util.converter.IntegerStringConverter;
import java.util.Arrays;
import java.util.function.UnaryOperator;
public class ChangeReaction extends Application {
@Override
public void start(Stage stage) {
final Style style = new Style();
final TextField alcoholPercentageField = new TextField();
final Label assessmentLabel = new Label();
PositiveIntTextFormatterFactory formatterFactory = new PositiveIntTextFormatterFactory();
TextFormatter<Integer> positiveIntFormatter = formatterFactory.createPositiveIntTextFormatter(0);
alcoholPercentageField.setTextFormatter(positiveIntFormatter);
positiveIntFormatter.valueProperty().addListener((observable, oldValue, newValue) -> {
if (newValue > 5) {
assessmentLabel.setText("Be careful");
style.getStatusStyleClassChooser().chooseStyleClass(
assessmentLabel,
Style.StatusStyleClassChoices.warning
);
} else {
assessmentLabel.setText("Smart choice");
style.getStatusStyleClassChooser().chooseStyleClass(
assessmentLabel,
Style.StatusStyleClassChoices.ok
);
}
});
VBox layout = new VBox(10,
alcoholPercentageField,
assessmentLabel
);
layout.setPadding(new Insets(10));
Scene scene = new Scene(layout);
scene.getStylesheets().add(Style.CSS);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
final class Style {
public static final String CSS = """
data:text/css,
.label.ok {
-fx-text-fill: green;
}
.label.warning {
-fx-text-fill: red;
}
""";
public enum StatusStyleClassChoices {
ok, warning;
};
private final StyleClassChooser<StatusStyleClassChoices> statusStyleClassChooser = new StyleClassChooser<>(StatusStyleClassChoices.class);
public StyleClassChooser<StatusStyleClassChoices> getStatusStyleClassChooser() {
return statusStyleClassChooser;
}
}
final class PositiveIntTextFormatterFactory {
private static final String POSITIVE_INT_PATTERN = "([1-9][0-9]*)?";
private final UnaryOperator<TextFormatter.Change> positiveIntFilter = change -> {
String newText = change.getControlNewText();
if (newText.matches(POSITIVE_INT_PATTERN) && newText.length() < 4) {
return change; // allow change
}
return null; // disallow change
};
private final StringConverter<Integer> positiveIntConverter = new IntegerStringConverter() {
@Override
public Integer fromString(String s) {
if (s.isEmpty()) return 0 ;
return super.fromString(s);
}
};
public TextFormatter<Integer> createPositiveIntTextFormatter(int defaultValue) {
return new TextFormatter<>(
positiveIntConverter,
defaultValue,
positiveIntFilter
);
}
}
final class StyleClassChooser<T extends Enum<T>> {
private final String[] choices;
public StyleClassChooser(Class<T> styleclassEnum) {
choices = Arrays.stream(styleclassEnum.getEnumConstants())
.map(Enum::toString)
.toArray(String[]::new);
}
public void chooseStyleClass(Node node, T styleClass) {
node.getStyleClass().removeAll(choices);
node.getStyleClass().add(styleClass.toString());
}
}