JavaFX: Apply text color to TableCell using custom style sheet?

11,646

Solution 1

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-colors. 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 ;
}

Solution 2

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:

Styled label

Share:
11,646
user1438038
Author by

user1438038

w5ewwx8k/3/ thrh54et43 mailinator com

Updated on June 24, 2022

Comments

  • user1438038
    user1438038 almost 2 years

    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;
    }
    
  • user1438038
    user1438038 over 9 years
    Unfortunately, none of this works for me. Neither when using setText(), nor when I add a Label and apply my style class to it. All I get is bold, but never colored text. Furthermore, I ran into issues with background color nor filling entire cell, when using a label.
  • José Pereda
    José Pereda over 9 years
    I've edited my answer, adding the full code of my example. Try to run it and see it works for you. Then fix your project accordingly. If still it doesn't work, update your question with the details necessary for us to reproduce your error.
  • user1438038
    user1438038 over 9 years
    Thank you. I had .table-row-cell:focused .text { -fx-fill: #000000; } set to have black font, when user selects a table row. That seems to interfer with my custom style. The information @James_D gave is very helpful in that respect, but I'm still figuring out... I'll get back to you, as soon as I fixed my issues.
  • user1438038
    user1438038 over 9 years
    Updated my question, I'm using a Label and VBox now. Works for me now, except for some issues I had with resetting the cell's background color.
  • user1438038
    user1438038 over 9 years
    Thank you @James_D, you provided very useful information. I've updated my question to show my current implementation. The way I defined .table-row-cell:focused .text was interfering with the custom style. Now that I set -fx-[light|mid|dark]-text-color, it is working. I had some issues with resetting the background color, though.
  • user1438038
    user1438038 over 9 years
    Though it did not solve my problem directly, I will accept your answer. You provided valuable information that helped me the most and finally made me master this issue. Thank you.