Search code examples
javajavafx-11

JavaFX 11 Editable ComboBox throws IndexOutOfBoundsException


I have been endeavoring to implement an event listener for an editable ComboBox which will add the edited item to the end of the list in an application, but this throws IndexOutOfBoundsException when the edited item is committed. I do not understand why this should be.

Consequently I have created a simplified application as follows and also tried enclosing the code which adds the edited item in a Platform.runLater() block but the issue persists.

Can anybody please suggest what may be causing this exception?

Thanks very much.

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;

public class ComboBoxTest extends Application {
    ComboBox<String> cbItems;
    Label lblResponse;

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

    public void start(Stage stage) {
        stage.setTitle("ComboBox Demo");
        FlowPane root = new FlowPane(10, 10);
        root.setAlignment(Pos.TOP_CENTER);
        Scene scene = new Scene(root, 240, 120);
        stage.setScene(scene);
        lblResponse = new Label();
        ObservableList<String> items =
                FXCollections.observableArrayList("item1", "item2", "item3", "item4", "item5");
        cbItems = new ComboBox<String>(items);
        cbItems.setValue("item1");
        cbItems.setEditable(true);
        lblResponse.setText("Selected item is " + cbItems.getValue());

        // Listen for action events on the combo box.
        cbItems.setOnAction(new EventHandler<ActionEvent>() {
            public void handle(ActionEvent ae) {
                lblResponse.setText("Selected item is " + cbItems.getValue());

                //add the modified item to the end of the list
                int i = cbItems.getSelectionModel().getSelectedIndex();
                if(!items.get(i).equals(cbItems.getValue())){
                    items.add(cbItems.getValue());
                }
            }
        });
        root.getChildren().addAll(cbItems, lblResponse);
        stage.show();
    }
}

Solution

  • Look at [the start of] the stack trace...

    Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length 5
        at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
        at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
        at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
        at java.base/java.util.Objects.checkIndex(Objects.java:359)
        at java.base/java.util.ArrayList.get(ArrayList.java:427)
        at javafx.base/com.sun.javafx.collections.ObservableListWrapper.get(ObservableListWrapper.java:89)
        at jfxprjct/jfxtests.ComboBoxTest$1.handle(ComboBoxTest.java:52)
    

    Now look at line 52 in file ComboBoxTest.java.
    (Note that it may be a different line number in your stack trace.)
    For me, this is line 52

    if(!items.get(i).equals(cbItems.getValue())){
    

    In other words, the value of i is -1 (minus one). And i is assigned a value in the line preceding the if statement.

    int i = cbItems.getSelectionModel().getSelectedIndex();
    

    In other words, there is no selected index. So you should first check the value of i and not assume that it is a valid index value.

    However, a better condition (in my opinion) would be

    if(!cbItems.getItems().contains(cbItems.getValue())){
    

    Then you don't need the selected index at all.