How to Set the Style of List View Cells Based on a Condition in Javafx

How to set the style of list view cells based on a condition in JavaFX

It is basically always an error to use a node subclass (such as Label) as the data type for controls such as ListView, TableView, etc. (The only exception I can think of is if you were writing a GUI builder, such as Scene Builder, where the data really were the actual GUI nodes. Even then you'd probably find it wouldn't work so well.) GUI node classes are very expensive to create; they typically have hundreds of properties and lots of overhead associated with styling and CSS parsing, event listeners, etc etc. Compare that to your actual data class, Consultation, which probably has a dozen properties or fewer and none of the other overhead. If you use a Label or other node class as the data type for your ListView, you create one node for every item in the list. The whole point of a ListView is that it only creates nodes for each visible cell, and reuses those cells. So if you have a large list, your approach will likely lead to performance issues. (The other point is that you are violating the separation of the view (the presentation) from the model (the data).)

So you should really have a ListView<Consultation> here, and use a cell factory to configure the text and style of the cells:

ListView<Consultation> notificationList = ... ;

notificationList.setCellFactory(lv -> new ListCell<Consultation>() {
@Override
protected void updateItem(Consultation c, boolean empty) {
super.updateItem(c, empty);
if (empty) {
setText(null);
setStyle("");
} else {
setText(": consult reminder - " + c.getReminderTime());

// Are you REALLY using an int for the reminderRead property??!?
// Surely this should be a boolean...

if (c.getReminderRead() == 1) { // I feel dirty just writing that
setStyle("-fx-background-color: #33CEFF");
} else {
setStyle("");
}
}
}
});

notificationList.getItems().setAll(notifications);

It's a little better to factor the style out into an external CSS file. You can use a CSS PseudoClass to turn the different styles on and off:

ListView<Consultation> notificationList = ... ;

PseudoClass reminderRead = PseudoClass.getPseudoClass("reminder-read");

notificationList.setCellFactory(lv -> new ListCell<Consultation>() {
@Override
protected void updateItem(Consultation c, boolean empty) {
super.updateItem(c, empty);
if (empty) {
setText(null);
} else {
setText(": consult reminder - " + c.getReminderTime());
}

// assumes Consultation.getReminderRead() returns a boolean...
pseudoClassStateChanged(reminderRead, c != null && c.getReminderRead())
}
});

notificationList.getItems().setAll(notifications);

Now in your external CSS file you can do:

.list-cell:reminder-read {
-fx-background-color: #33CEFF ;
}

Java FX How to style an item in a ListView if changed

Create something you can observe, and observe it...

E.g. if your usersService has an ObservableList<String> onlineUsers, containing a list of the usernames of everyone who's online, you can do:

friends.setCellFactory(lv -> {

ListCell<String> cell = new ListCell<String>() {

@Override
protected void updateItem(String friendname, boolean empty) {
super.updateItem(friendname, empty);
if (empty) {
setText(null);
setStyle("");
} else {
setText(friendname);
}
}

};

cell.styleProperty().bind(new StringBinding() {
{ bind(cell.itemProperty(), usersService.getOnlineUsers()); }
@Override
protected String computeValue() {
if (cell.getItem() == null) {
return "" ;
}
if (usersService.getOnlineUsers().contains(cell.getItem())) {
return "/* online style here */";
}
return "/* offline style here*/" ;
}
});

return cell ;

});

Then whenever the list of online users changes, the cell will update accordingly.

You can simplify this by using an external CSS file and a custom PseudoClass:

PseudoClass online = PseudoClass.getPseudoClass("online");

friends.setCellFactory(lv -> {

ListCell<String> cell = new ListCell<String>() {

@Override
protected void updateItem(String friendname, boolean empty) {
super.updateItem(friendname, empty);
setText(friendname);
}

};

InvalidationListener listener = obs ->
cell.pseudoClassStateChanged(online,
cell.getItem() != null
&& usersService.getOnlineUsers().contains(cell.getItem()));
cell.itemProperty().addListener(listener);
usersService.getOnlineUsers().addListener(listener);

return cell ;

});

Then in your CSS file do

.list-cell {
/* offline style rules here */
}
.list-cell:online {
/* online style rules here */
}

why cant i modify list-cell css within ListView?

I got a fix, i dont have an explanation though:

Changed

.list-cell{
-fx-cursor: hand;
}

To

.list{
}
.list-cell:filled:hover{
-fx-cursor: hand;
-fx-text-fill: dimgrey;
}

My first css element seems to be ignored somehow, the rest of them gets activated

JavaFx list View coloring the list cell

Understanding the dynamic nature of ListView cell coloring

Normally, the inner cell color changes based upon a number of things:

  1. Whether the cell is an odd or even row (even rows have a lighter background).
  2. Whether the row is selected.
  3. Selected rows have different colors depending upon whether the control has focus or not (a focused selected row is blue, an unfocused selected row is gray).

So because the cell color can change based upon the state, you need to decide if you want to retain this behavior or not when you set custom coloring for your cells.

Sample Solution

Here is an example which will set the cell color to a pale green color depending upon whether a list item matches a criteria (in this case hardcoded for testing purposes to a name beginning with the letter 'J').

sample

import javafx.application.Application;
import javafx.collections.*;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;

public class ColoredList extends Application {
private static final ObservableList<String> data = FXCollections.observableArrayList(
"Jill",
"Jack",
"Tom",
"Harry",
"Jenney"
);

private static final String DEFAULT_CONTROL_INNER_BACKGROUND = "derive(-fx-base,80%)";
private static final String HIGHLIGHTED_CONTROL_INNER_BACKGROUND = "derive(palegreen, 50%)";

@Override
public void start(Stage stage) throws Exception {
ListView<String> listView = new ListView<>(data);
listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
@Override
public ListCell<String> call(ListView<String> param) {
return new ListCell<String>() {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);

if (item == null || empty) {
setText(null);
setStyle("-fx-control-inner-background: " + DEFAULT_CONTROL_INNER_BACKGROUND + ";");
} else {
setText(item);
if (item.startsWith("J")) {
setStyle("-fx-control-inner-background: " + HIGHLIGHTED_CONTROL_INNER_BACKGROUND + ";");
} else {
setStyle("-fx-control-inner-background: " + DEFAULT_CONTROL_INNER_BACKGROUND + ";");
}
}
}
};
}
});

VBox layout = new VBox(listView);
layout.setPadding(new Insets(10));

stage.setScene(new Scene(layout));
stage.show();
}

public static void main(String[] args) {
launch(ColoredList.class);
}
}

Some things to note:

  1. The custom cell rendering is accomplished via a custom cell factory for the ListView.
  2. The cells returned by the cell factory, set the -fx-control-inner-background looked-up CSS color depending upon the cell state. If you don't know what a looked-up color is, look it up in the the JavaFX CSS reference guide and look at the modena.css file in the jfxrt.jar file that ships with your Java install.
  3. The default cell rendering mechanism will slightly darken or lighten even and odd rows in the ListView, even based upon your custom color.
  4. The sample only sets custom colors for non-selected rows. For selected rows, the default blue and gray colors are retained. So the sample is not comprehensive, but hopefully provides enough information for you to accomplish what you want.
  5. The sample codes the default and custom colors in code, but a larger app would be better off having these defined in a separate user CSS stylesheet.

How to style custom control inside a ListView in JavaFX

Theoretically this set of selectors do the trick:

// Color of the list-cell selected + unselected: transparent
.list-view .list-cell,
.list-view .list-cell:filled,
.list-view .list-cell:selected,
.list-view .list-cell:focused,
.list-view .list-cell:hover {
-fx-background-color: transparent;
-fx-effect: null;
}

// Color of the custom control hover
.list-view .list-cell:hover .email-view {
-fx-background-color: greenyellow;
}

// Color of the custom control selected
.list-view .list-cell:filled:selected:focused .email-view,
.list-view .list-cell:filled:selected .email-view {
-fx-background-color: green;
}

// Color of the custom control unselected
.email-view { -fx-background-color: gray; }

The goal is to kep the list-cell transparent all the time, and based on the pseudo-state of the list-cell set the background color of email-view. Maybe I forgot about some state, but it could be a good starting point.

How do I dynamically change the background of an item in a listview in JavaFX

You can use a custom cell factory for the ListView that checks for the condition and applies the appropriate css style class to each item/cell.

The following code shows how to go about that for a Listview with items of type String.

     listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>(){

@Override
public ListCell<String> call(ListView<String> p) {

ListCell<String> cell = new ListCell<String>(){

@Override
protected void updateItem(String t, boolean bln) {
super.updateItem(t, bln);
if (t != null ) {
setText( t);

if (item_is_not_available){

if (!getStyleClass().contains("mystyleclass") {
getStyleClass().add("mystyleclass");
}

} else {
getStyleClass().remove("mystyleclass");
}
} else {
setText("");
}
}

};

return cell;
}
});

In your css file, a possible definition of mystyleclass could look like this (displaying items that are not available with a red background):

.mystyleclass{
-fx-background-color: #ff0000;
}


Related Topics



Leave a reply



Submit