-1

I'm creating a cinema system, whereby the user should be able to select a film through a combobox in the Homepage. I have created an Array list in the FilmController Class and then converted it to an observableList and am struggling to populate its contents within the combobox (HomepageController).

This is the FilmController with the arraylist & observableList

public class FilmController {

private ArrayList<Film> films = new ArrayList<>();

public FilmController() {
    Film f1 = new Film("Avatar", "James Cameron", "James Cameron",
            "Sam Wortherington" + "Zoe Saldana", "162", "Sci-Fi", 8.50);

    Film f2 = new Film("Black Panther", "Kevin Feige", "Ryan Coogler",
            "Chadwick Boseman" + "Lupita Nyong'o", "135", "Sci-Fi Fantasy", 10.00);

    Film f3 = new Film("Creed 2", "Sylvester Stallone", "Steven Caple Jr",
            "Michael B Jordan" + "Tessa Thompson", "130", "Drama", 10.00);

    Film f4 = new Film("Deadpool", "Simon Kinberg", "Tim Miller",
            "Ryan Reynolds" + "Morena Baccarin", "109", "Sci-Fi", 7.50);

    Film f5 = new Film("A Quiet Place", "Michel Bay", "John Krasinski",
            "Emily Blunt" + "John Krasinski", "91", "Thriller", 8.00);

    films.add(f1);
    films.add(f2);
    films.add(f3);
    films.add(f4);
    films.add(f5);
}

public ArrayList<Film> getFilms() {
    return films;
}

public ObservableList<Film> getOlFilms() {
    return FXCollections.observableArrayList(films);

   }
}

I tried implementing this in the HomepageController, but it seem to be giving me an error:

  public class HomepageController {

        public ComboBox cbFilms;

public void initialize() { 
cbFilms.setButtonCell((ListCell) cbFilms.getCellFactory().call(null)); 
}

public void cbListFilms(ActionEvent actionEvent) {
        FilmController f = new FilmController();
        cbFilms.setItems(f.getOlFilms().toArray());
    } 
}

I have looked at this question, however it doesn't seem to be working for me.

I'd like the combobox to list the film name only, and preferably when the mouse hovers over the film name I'd like it to display the remaining attributes if possible.

This is the fxml content for the combobox:

    <ComboBox fx:id="cbFilms" layoutX="291.0" layoutY="138.0" onAction="#cbListFilms" prefHeight="31.0" prefWidth="230.0"
          promptText="Please select film by name" style="-fx-background-color: tan;">
    <items>
        <FXCollections fx:factory="observableArrayList"/>
    </items>
    <effect>
        <SepiaTone/>
    </effect>
</ComboBox>
  • 1
    Why do you use `toArray`? If you want to use a `ObservableList` containing the contents, you should remove the call to this method. If you simply want to copy the contents of the collection to the items list, use `cbFilms.getItems().setAll(...);` – fabian Mar 13 '19 at 23:05
  • 1
    The question you linked to is for Swing, but you've tagged this question as JavaFX. Which is it? Please read the [ask] guide and [edit] your question accordingly. – Zephyr Mar 14 '19 at 00:12
  • Sorry for the confusion, I am working with JavaFX – Karishma Parmar Mar 14 '19 at 13:10

2 Answers2

2

You should set a CellFactory, which handles all the items in your ComboBox:

ComboBox<Film> comboBox = new ComboBox<>();
comboBox.setItems(f.getOlFilms());
comboBox.setCellFactory(param -> new ListCell<Film>() {
    @Override
    protected void updateItem(Film item, boolean empty) {
        super.updateItem(item, empty);
        if (item != null) {
            setText(item.getName());
        } else {
            // handle null object
        }
    }
});

Additionally you should set a ButtonCell for the selected item. You can either create an own ListCell for that to add some more data in the view or use the same as for the items:

comboBox.setButtonCell(comboBox.getCellFactory().call(null));

As an alternative to the null value you can pass a default value there.

Samuel Philipp
  • 10,631
  • 12
  • 36
  • 56
  • 1
    Sorry, what is a ListCell, I haven't come across that before, and would that also be in the HomepageController? I have set the CellFaactory and I am no longer getting an error, however the list is still not being displayed in the combobox – Karishma Parmar Mar 13 '19 at 23:10
  • You should set this after the comboBox is created. If you are using fxml do this in the initialize method in your controller. – Samuel Philipp Mar 13 '19 at 23:15
  • I have added this and an initialize method in my Controller class, however I am receiving the error: Caused by: javafx.fxml.LoadException: – Karishma Parmar Mar 13 '19 at 23:20
  • Can you add the content of your fxml and the complete stacktrace to your question please? – Samuel Philipp Mar 13 '19 at 23:23
  • It seems that the exception you shared has nothing to do with my answer. Your fxml file seems to be broken, because the ComboBox has no closing tag. – Samuel Philipp Mar 14 '19 at 06:02
2

You do not need/want to call the .toArray() method on the ObservableList. The ComboBox.setItems() method takes an ObservableList just fine:

cbFilms.setItems(f.getOlFilms());

Then, in order to have the ComboBox display only the film's title, you need to set a StringConverter on the ComboBox. The StringConverter basically takes the object and returns a String representation of it:

cbFilms.setConverter(new StringConverter<Film>() {
    @Override
    public String toString(Film film) {
        return film.getTitle();
    }

    @Override
    public Film fromString(String string) {
        return null;
    }
});

Here is a complete example you can test out:

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;

public class SimpleComboBoxExample extends Application {

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

    @Override
    public void start(Stage primaryStage) {

        // Simple interface
        VBox root = new VBox(5);
        root.setPadding(new Insets(10));
        root.setAlignment(Pos.CENTER);

        // Create some sample data
        ObservableList<Film> filmsList = FXCollections.observableArrayList();
        filmsList.add(new Film("Titanic", "James Cameron"));
        filmsList.add(new Film("Jaws", "Steven Spielberg"));
        filmsList.add(new Film("The Last Samurai", "Edward Zwick"));

        // Define our ComboBox with our data model as its type
        ComboBox<Film> cboFilms = new ComboBox<>();

        // Use a StringConverter to configure our ComboBox to display only the film's title
        cboFilms.setConverter(new StringConverter<Film>() {
            @Override
            public String toString(Film film) {
                return film.getTitle();
            }

            @Override
            public Film fromString(String string) {
                return null;
            }
        });

        // Finally, set our ComboBox's items to our sample list
        cboFilms.setItems(filmsList);

        root.getChildren().add(cboFilms);

        // Show the Stage
        primaryStage.setWidth(300);
        primaryStage.setHeight(300);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
}

// Our data model class
class Film {

    private StringProperty title = new SimpleStringProperty();
    private StringProperty director = new SimpleStringProperty();

    public Film(String title, String director) {
        this.title.set(title);
        this.director.set(director);
    }

    public String getTitle() {
        return title.get();
    }

    public StringProperty titleProperty() {
        return title;
    }

    public String getDirector() {
        return director.get();
    }

    public StringProperty directorProperty() {
        return director;
    }
}

The Result:

screenshot

Zephyr
  • 9,885
  • 4
  • 28
  • 63