JavaFX: How can I apply text color to a TableCell using a custom style sheet?
It works fine, when I use setTextFill()
in my CellFactory directly, but I want to apply custom style using an external CSS file. I could prove that my CSS class is applied, since the font becomes bold. The CSS file's font color, however, is not applied.
@Override
protected void updateItem(MyObject item, boolean empty) {
super.updateItem(item, empty);
if (null != item) {
// EITHER:
this.getStyleClass().add("styleImportant"); // Does NOT set color.
// OR:
this.setTextFill(Color.RED); // Does set color.
}
else {
this.getStyleClass().remove("styleImportant");
}
}
Style sheet:
.styleImportant {
-fx-font-weight: bold; /** Does work. */
-fx-text-fill: red; /** Does NOT work. */
}
It is somehow related to specificity of CSS selectors, but I did not manage to find any valid setup.
Edit: I managed to apply both, a custom text color and background color, using CSS. My implementation now uses a Label
that is wrapped in a VBox
to make the background color fill the entire table cell. However, I still had some issues with background color not being cleared when removing the custom style.
Is there any better solution than applying a clear style?
colExample.setCellFactory(new Callback<TableColumn<Example, Example>, TableCell<Example, Example>>() {
@Override
public TableCell<Example, Example> call(TableColumn<Example, Example> tableColumn) {
return new TableCell<Example, Example>() {
private VBox container;
private Label text;
// Anonymous constructor
{
this.container = new VBox();
this.text = this.createLabel();
this.container.getChildren().add(this.text);
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
this.setStyle("-fx-padding: -1 -1 -1 -1;"); // Remove padding from cell
this.setGraphic(this.container);
}
private final Label createLabel() {
Label label = new Label();
VBox.setVgrow(label, Priority.ALWAYS);
label.setMaxWidth(Double.MAX_VALUE);
label.setMaxHeight(Double.MAX_VALUE);
label.setAlignment(Pos.CENTER);
return label;
}
@Override
protected void updateItem(Example example, boolean empty) {
super.updateItem(example, empty);
// Reset column styles
if (null != this.text && null != this.text.getStyleClass()) {
String[] possibleStyles = new String[] { "styleImportant", "clearStyle" };
for (String style: possibleStyles) {
if (this.text.getStyleClass().contains(style)) {
// Will not reset background, even though style is removed?
this.text.getStyleClass().remove(style);
}
}
// Apply reset style to clear background color
this.text.getStyleClass().add("clearStyle");
}
if (null != example) {
this.text.setText(example.getContent());
if (example.isImportant()) {
this.text.getStyleClass().add("styleImportant");
}
}
}
};
}
});
My style sheet:
/** Keep black text color, when user selects row */
.table-row-cell:focused {
-fx-dark-text-color: #000000;
-fx-mid-text-color: #000000;
-fx-light-text-color: #000000;
}
/** Style to reset background color */
.clearStyle {
-fx-background-color: transparent;
}
/** Style for important cells */
.styleImportant {
/** Red text color on any background */
-fx-dark-text-color: #FF0000;
-fx-mid-text-color: #FF0000;
-fx-light-text-color: #FF0000;
-fx-background-color: #FF9999;
}
As José points out in his answer, if you are setting a graphic in your cell, you (probably) need to apply the css style class to the graphic (depending on what that graphic is). If you are simply calling setText(...)
your code should work.
The reason that a Label
set as the graphic doesn't inherit -fx-text-fill
from the table cell is that the Label
also has a setting for -fx-text-fill
. In the default stylesheet, both TableCell
and Label
have this set as follows:
-fx-text-fill: -fx-text-background-color ;
fx-text-background-color
is a looked-up color that is defined as a ladder, as follows:
-fx-text-background-color: ladder(
-fx-background,
-fx-light-text-color 45%,
-fx-dark-text-color 46%,
-fx-dark-text-color 59%,
-fx-mid-text-color 60%
);
This (fairly complex) setting means that the value of -fx-text-background-color
depends on the value of -fx-background
. If -fx-background
is less than 45% of maximum intensity (i.e. it's dark), the value of -fx-text-background-color
is set to -fx-light-text-color
. If -fx-background
is between 46% and 59% intensity, the value is equal to -fx-drak-text-color
. If it is 60% or more, it's set to -fx-mid-text-color
. The idea here is that the text color will automatically adjust to the background to remain visible. The values of -fx-dark-text-color
, -fx-mid-text-color
, and -fx-light-text-color
are set to black, a dark gray (#333), and white, respectively.
Since Label
does not override the value of -fx-text-background-color
, you can achieve what you need by just changing the value of that for your table cell:
.styleImportant {
-fx-text-background-color: red ;
}
Now this overrides the looked-up color value for the table cell, and since the graphic inside the cell doesn't override that value itself, it inherits it from the cell.
A more sophisticated way to do this is to redefine the -fx-[light|mid|dark]-text-color
s. The advantage of this is that the colors will adjust appropriately if you change the background: in particular if the cell is selected you can ensure that the text stays visible:
.styleImportant {
-fx-light-text-color: white ;
-fx-mid-text-color: #c00 ;
-fx-dark-text-color: red ;
}
Since I don't know what type of object is in the cell, I'll use a Label
.
This is what you are doing:
@Override
public void start(Stage primaryStage) {
TableView<Label> table = new TableView<>();
TableColumn<Label,String> column = new TableColumn<>();
column.setCellValueFactory(param -> param.getValue().textProperty());
column.setCellFactory((TableColumn<Label, String> param) -> {
TableCell<Label, String> cell = new TableCell<Label, String>(){
@Override
protected void updateItem(String item, boolean empty){
super.updateItem(item, empty);
if(!empty){
this.getStyleClass().add("styleImportant");
Label label = new Label(item);
setGraphic(label);
}
}
};
return cell;
});
table.getColumns().add(column);
...
}
This will give you bold text, but black.
If you want to have an object (in my case, a Label
) with red text, apply the style sheet to the object:
@Override
protected void updateItem(String item, boolean empty){
super.updateItem(item, empty);
if(!empty){
Label label = new Label(item);
label.getStyleClass().add("styleImportant");
setGraphic(label);
}
}
EDIT
This is the full code of the example:
@Override
public void start(Stage primaryStage) {
TableView<Label> table = new TableView<>();
ObservableList<Label> data = FXCollections.observableArrayList(
new Label("Some Text"), new Label("Some More Text"));
TableColumn<Label,String> column = new TableColumn<>("Column");
column.setCellValueFactory(param -> param.getValue().textProperty());
column.setCellFactory((TableColumn<Label, String> param) -> {
TableCell<Label, String> cell = new TableCell<Label, String>(){
@Override
protected void updateItem(String value, boolean empty){
super.updateItem(value, empty);
if(!empty){
Label label = new Label(value);
label.getStyleClass().add("styleImportant");
setGraphic(label);
} else {
setGraphic(null);
}
}
};
return cell;
});
column.setPrefWidth(100);
table.getColumns().add(column);
table.setItems(data);
Scene scene = new Scene(table, 300, 250);
scene.getStylesheets().add(getClass().getResource("table.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
where in table.css:
.styleImportant {
-fx-font-weight: bold;
-fx-text-fill: red;
}
Running this short sample should look like this:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With