Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javafx ComboBox disappearing items after select

I have created a (JavaFX) combobox, which I am populating with an observable list made from HBoxes, so that I can display an image with some text in each list cell.

This displays well, other than the fact that whenever you select one of the items in the list, it will disappear. Once you have selected every item, it will not render any items at all. (You can still select them by clicking in the space where they previously were.

Do you know how I might correct this, please?

enter image description hereenter image description here

Part of my code is displayed below:

public class IconListComboBox {

Group listRoot = new Group();
VBox mainVBox = new VBox();
ComboBox selectionBox = new ComboBox();
List<HBox> list = new ArrayList<HBox>();
ListView<HBox> listView = new ListView<HBox>();
ObservableList<HBox> observableList;



public IconListComboBox(int dimensionX, int dimensionY, ArrayList<String> names, ArrayList<ImageView> icons)
{

    //VBox.setVgrow(list, Priority.ALWAYS);
    selectionBox.setPrefWidth(dimensionY);
    selectionBox.setPrefHeight(40);

    for(int i = 0; i < names.size(); i++)
    {
        HBox cell = new HBox();
        Label name = new Label(names.get(i));
        Label icon = new Label();
        icon.setGraphic(icons.get(i));
        name.setAlignment(Pos.CENTER_RIGHT);
        icon.setAlignment(Pos.CENTER_LEFT);
        icon.setMaxWidth(Double.MAX_VALUE);
        HBox.setHgrow(icon, Priority.ALWAYS);
        cell.getChildren().add(icon);
        cell.getChildren().add(name);
        list.add(cell);


    }

    observableList = FXCollections.observableList(list);
    listView.setItems(observableList);

    listView.setPrefWidth(dimensionX);
    selectionBox.setMaxWidth(dimensionX);
    listView.setMaxWidth(dimensionX);

    selectionBox.setItems(observableList);

    mainVBox.getChildren().add(selectionBox);
    mainVBox.getChildren().add(listRoot);
    //mainVBox.getChildren().add(listView);
    //listRoot.getChildren().add(listView);


}

Thanks in advance for your assistance!

like image 693
Ben Hayward Avatar asked Oct 11 '14 20:10

Ben Hayward


2 Answers

Ok, so I've managed to work this out, thanks to @James_D 's very kind help!

This is for anyone who, like me, was slightly daunted by the example that was given in the Java documentation. (Although, my description below is probably worse!!)


So, I started by adding an HBox which was in the layout I wanted straight into the ComboBox... which is a bad idea!

So, before you go deleting everything you've done, save the HBox somewhere, and do the following:

1. Create a new class to hold your date (Image, and String) which will go into each cell. Make getters/setters to do this. I called mine IconTextCell.

2. Add the following code to the class where your ComboBox is located:

yourComboBox.setCellFactory(new Callback<ListView<T>, ListCell<T>>() {

    @Override public ListCell<T> call(ListView<T> p) {
        return new ListCell<T>() {
            Label name = new Label();
            Label icon = new Label();
            private final HBox cell;
            { 
                setContentDisplay(ContentDisplay.GRAPHIC_ONLY); 
                cell = new HBox();

                //HERE, ADD YOUR PRE-MADE HBOX CODE

                name.setAlignment(Pos.CENTER_RIGHT);
                icon.setAlignment(Pos.CENTER_LEFT);
                icon.setMaxWidth(Double.MAX_VALUE);
                HBox.setHgrow(icon, Priority.ALWAYS);
                cell.getChildren().add(icon);
                cell.getChildren().add(name);
            }

            @Override protected void updateItem(T item, boolean empty) {
                super.updateItem(item, empty);

                if (item == null || empty) {
                    setGraphic(null);
                } else {
                    name.setText(item.getLabel());
                    icon.setGraphic(item.getIcon());
                    setGraphic(cell);
                    //HERE IS WHERE YOU GET THE LABEL AND NAME
                }
           }
      };
  }
});

You'll see that the main content is very similar to what I had already produced, so no code is lost. Just replace "T" with your own class for representing a cell.

3. This will display your icon and string in the list, but you need to to also be displayed in the button (the grey top selector part of the combobox, aka the button). Do do this, we need to add the following code:

class IconTextCellClass extends ListCell<T> {
    @Override
    protected void updateItem(T item, boolean empty) {
        super.updateItem(item, empty);
        if (item != null) {
            setText(item.getLabel());
        }
    }
};

selectionBox.setButtonCell(new IconTextCellClass());

...and that's how I did it. I hope this helps - please compare this to my original post. The actual content (where I create the HBox etc) is obviously not generalised. You can make this as simple or as complex as you want.

Once again, thanks for your help! I hope this post helps others!

like image 126
Ben Hayward Avatar answered Sep 24 '22 17:09

Ben Hayward


This is exactly the example cited in the documentation under "A warning about inserting Nodes into the ComboBox items list".

The list of items in the combo box should represent data - not the UI component used to display the data. The issue is that the HBox cannot appear twice in the scene graph: so it cannot appear both in the "selected cell" and as a cell in the drop-down list.

Instead, create a class that represents the data you are displaying in the ComboBox, and use a cell factory to instruct the ComboBox as to how to display those data. Be sure to set a button cell as well (the cell used for the selected item).

like image 39
James_D Avatar answered Sep 22 '22 17:09

James_D