2

I have the following problem:

I have ListView<ViewsRecord> where ViewsRecord has property int favorites. If favorites > 0 it's in favorites, and if favorites == 0 it's a regular row (not favorite).

Now what I want to do is:

  1. When user selects cells (in multiple select mode) those cells will have default selected background (like: -fx-background-color: -fx-selection-bar)
  2. When cells aren't selected by the user:

    2.1. if cell isn't in favorites it has regular bacground (ex. white)

    2.2. if cell is in favorites it has green background

So far I came up with this solution, but it's ugly code, and I wonder if there's easiest way to do that. Further more, I had to use setUserData() to check if cell should be selected or not, otherwise during the list scroll, or select - cells had randomly changed their colors. (I assume it's because reusable objects are stored in memory and updateItem() isn't always fired).

Here's my code:

list.setCellFactory(new Callback<ListView<ViewsRecord>, ListCell<ViewsRecord>>(){
            @Override
            public ListCell<ViewsRecord> call(ListView<ViewsRecord> param) {
                ListCell<ViewsRecord> cell = new ListCell<ViewsRecord>(){
                         @Override
                        protected void updateItem(ViewsRecord item, boolean empty) {
                            super.updateItem(item, empty);
                            if(!empty){
                                if(item.getFavorites() > 0){  //favorite view
                                    if(!isSelected()){
                                        setStyle("-fx-background-color: darkseagreen;");
                                    }
                                    setUserData(new Integer(1));
                                } else {  //normal view
                                    if(!isSelected()){
                                        setStyle("-fx-background-color: white;");
                                    }
                                    setUserData(new Integer(0));
                                }
                                setText(item.toString());
                            } else { //empty view
                                setText(null); 
                                setStyle("-fx-background-color: white;");
                                setUserData(new Integer(0)); 
                            }
                        }
                    };
                //fix bacground color when cell is selected
                cell.selectedProperty().addListener( (obsVal, oldVal, newVal) -> {
                    if(newVal){
                        cell.setStyle("-fx-background-color: -fx-selection-bar;");
                    } else {
                        if((Integer)cell.getUserData() == 1){ //favorite
                            cell.setStyle("-fx-background-color: darkseagreen;");
                        } else { //normal
                            cell.setStyle("-fx-background-color: white;");
                        }
                    }
                });
                return cell;
            }   
        });

EDIT

Thanks to jns I've managed to simplify the code. Current version:

list.setCellFactory(new Callback<ListView<ViewsRecord>, ListCell<ViewsRecord>>(){   
            @Override
            public ListCell<ViewsRecord> call(ListView<ViewsRecord> param) {
                final PseudoClass FAVORITE_PSEUDO_CLASS = PseudoClass.getPseudoClass("favorite");
                ListCell<ViewsRecord> cell = new ListCell<ViewsRecord>(){
                         @Override
                        protected void updateItem(ViewsRecord item, boolean empty) {
                            if(!empty){
                                //favorite or not, and not selected
                                pseudoClassStateChanged(FAVORITE_PSEUDO_CLASS, (item.getFavorites() > 0) && !isSelected());
                                setText(item.toString());
                            } else { 
                                setText(null); 
                                //empty
                                pseudoClassStateChanged(FAVORITE_PSEUDO_CLASS, false);
                            }
                            super.updateItem(item, empty);
                         }
                };
                cell.selectedProperty().addListener( (obsVal, oldVal, newVal) -> {
                    if(newVal){
                        //selected
                        cell.pseudoClassStateChanged(FAVORITE_PSEUDO_CLASS, false); 
                    } else {
                        //favorite or not
                        cell.pseudoClassStateChanged(FAVORITE_PSEUDO_CLASS, cell.getItem().getFavorites() > 0); 
                    }
                });
                return cell;
            }
        });
fabian
  • 80,457
  • 12
  • 86
  • 114
DominikStyp
  • 360
  • 6
  • 10

2 Answers2

1

You could use a PseudoClass for styling the listcell according to the favorite property.

public class YourListCell extends ListCell<ViewsRecord> {

private static PseudoClass FAVORITE_PSEUDO_CLASS = PseudoClass.getPseudoClass("favorite");

    @Override
     protected void updateItem(ViewsRecord item, boolean empty) {
         super.updateItem(item, empty);
         if (!empty) {
             boolean isFavorite = item > 0;              
             pseudoClassStateChanged(FAVOURITE_PSEUDO_CLASS, isFavorite);
             ...
     }
}

This allows you to style your listcell via css in your stylesheet:

.list-cell:favorite {
-fx-background-color: darkseagreen;
}
jns
  • 6,017
  • 2
  • 23
  • 28
1

ListCell contains a getItem() method. Thus you can get access to your data object. Furthermore you can use its PseudoClass empty and selected to transfer more of the style handling to the stylesheet:

.list-cell, .list-cell:empty {
    -fx-background-color: white
    }

.list-cell:favorite{
    -fx-background-color: darkseagreen;
}

.list-cell:selected {
    -fx-background-color: -fx-selection-bar;
}
jns
  • 6,017
  • 2
  • 23
  • 28