Reputation: 21
I'm trying to change the color and text of a Label conditionally.
The text change works, but the color change does not.
I get an error:
The method setFill(Color) is undefined for the type When.StringConditionBuilder
Code snippet:
assessmentLabel.textProperty().bind(
new When(alcoholPercentageField.textProperty().greaterThanOrEqualTo("5"))
.then("Be careful").setFill(Color.RED)
.otherwise("Smart choice").setFill(Color.GREEN)
);
Any way to do this?
Upvotes: 1
Views: 1470
Reputation: 159281
Use a change listener, not a binding
You are trying to use a single bind to change multiple target properties. You can’t do that.
You could create multiple bindings to change multiple properties, but I wouldn’t advise that.
Instead, place a change listener on the property you wish to listen to (in this case alcoholPercentageField.textProperty()) and in the code block of the listener, check the status of the new value and update all related properties (in this case the assessment label text and fill).
Aside
Text comparison of numeric values is not a good idea, even if might work in some single-digit cases. Instead, you should convert the text to a number to compare it with another number.
Use CSS and style classes rather than setting fills directly.
When using bindings, always check to see if you can make use of the helper functions in the Bindings class, such as Bindings.when().
When
.Simple Solution
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();
}
}
More Complex Solution
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());
}
}
Upvotes: 2