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:
- Whether the cell is an odd or even row (even rows have a lighter background).
- Whether the row is selected.
- 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').
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:
- The custom cell rendering is accomplished via a custom cell factory for the ListView.
- 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 themodena.css
file in the jfxrt.jar file that ships with your Java install. - The default cell rendering mechanism will slightly darken or lighten even and odd rows in the ListView, even based upon your custom color.
- 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.
- 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
Background Timer Task in Jsp/Servlet Web Application
Mapstruct and Lombok Not Working Together
Prevent Android Activity Dialog from Closing on Outside Touch
How to Remove All Callbacks from a Handler
Android Firestore Convert Array of Document References to List<Pojo>
Android Days Between Two Dates
Java Lang Unsupportedclassversion Error in Xamarin Studio
Android.Content.Res.Resources$Notfoundexception
Java.Sql.Sqlexception: No Suitable Driver Found for Jdbc:Microsoft:Sqlserver
Why Do I Need Transaction in Hibernate for Read-Only Operations
How to Set Background Color of an Activity to White Programmatically
How to Allow Users to Check for the Latest App Version from Inside the App
Code Will Only Return 0.0, 0.0 Gps Coordinate While Throwing Nullpointerexception