JavaFX ComboBox Change Value Raises IndexOutOfBoundsException

I want to enable validations for my combobox to restrict access to some values. I could just remove these inaccessible items from the list, yes, but I would like the user to see other options, even if he cannot select them (yet).

Problem: choosing a different value in the change list throws an IndexOutOfBoundsException, and I have no idea why.

Here is the SSCCE. It creates a ComboBox with integer values, and the first one is selected by default. Then I tried to save it very simply: every change in the value is considered โ€œwrongโ€, and I change the selection to the first element. But still, IndexOutOfBounds:

import javafx.application.Application; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.ComboBox; import javafx.stage.Stage; public class Tester extends Application{ public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) throws Exception { ComboBox<Integer> box = new ComboBox<Integer>(); ObservableList<Integer> vals= FXCollections.observableArrayList(0,1,2,3); box.setItems(vals); box.getSelectionModel().select(0); /*box.valueProperty().addListener((observable, oldValue, newValue) -> { box.getSelectionModel().select(0); });*/ /*box.getSelectionModel().selectedItemProperty().addListener((observable,oldValue,newValue)->{ System.out.println(oldValue+","+newValue); box.getSelectionModel().select(0); });*/ box.getSelectionModel().selectedIndexProperty().addListener((observable,oldValue,newValue)->{ System.out.println(oldValue+","+newValue); box.getSelectionModel().select(0); }); Scene scene = new Scene(new Group(box),500,500); stage.setScene(scene); stage.show(); } } 

I tested it with valueProperty, selectedItemProperty and selectedIndexProperty, as well as all of them:

 box.getSelectionModel().select(0); box.getSelectionModel().selectFirst(); box.getSelectionModel().selectPrevious(); box.setValue(0); if (oldValue.intValue() < newValue.intValue()) box.getSelectionModel().select(oldValue.intValue()); 

The only thing that works is setting the value itself:

 box.getSelectionModel().select(box.getSelectionModel().getSelectedIndex()); box.setValue(box.getValue)); 

Here is the exception:

 Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException at com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList.subList(Unknown Source) at javafx.collections.ListChangeListener$Change.getAddedSubList(Unknown Source) at com.sun.javafx.scene.control.behavior.ListViewBehavior.lambda$new$178(Unknown Source) at com.sun.javafx.scene.control.behavior.ListViewBehavior$$Lambda$126/644961012.onChanged(Unknown Source) at javafx.collections.WeakListChangeListener.onChanged(Unknown Source) at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(Unknown Source) at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(Unknown Source) at com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList.callObservers(Unknown Source) at javafx.scene.control.MultipleSelectionModelBase.clearAndSelect(Unknown Source) at javafx.scene.control.ListView$ListViewBitSetSelectionModel.clearAndSelect(Unknown Source) at com.sun.javafx.scene.control.behavior.CellBehaviorBase.simpleSelect(Unknown Source) at com.sun.javafx.scene.control.behavior.CellBehaviorBase.doSelect(Unknown Source) at com.sun.javafx.scene.control.behavior.CellBehaviorBase.mousePressed(Unknown Source) at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(Unknown Source) at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(Unknown Source) at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(Unknown Source) at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source) at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source) at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source) at javafx.event.Event.fireEvent(Unknown Source) at javafx.scene.Scene$MouseHandler.process(Unknown Source) at javafx.scene.Scene$MouseHandler.access$1500(Unknown Source) at javafx.scene.Scene.impl_processMouseEvent(Unknown Source) at javafx.scene.Scene$ScenePeerListener.mouseEvent(Unknown Source) at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source) at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$350(Unknown Source) at com.sun.javafx.tk.quantum.GlassViewEventHandler$$Lambda$172/2037973250.get(Unknown Source) at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(Unknown Source) at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(Unknown Source) at com.sun.glass.ui.View.handleMouseEvent(Unknown Source) at com.sun.glass.ui.View.notifyMouse(Unknown Source) at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) at com.sun.glass.ui.win.WinApplication.lambda$null$145(Unknown Source) at com.sun.glass.ui.win.WinApplication$$Lambda$36/2117255219.run(Unknown Source) at java.lang.Thread.run(Unknown Source) 

What am I doing wrong?

+6
source share
2 answers

In JavaFX, you cannot change the contents of an ObservableList while the change is already in progress. What happens here is that your listeners (any of them you are trying to) get fired as part of the change to box.getSelctionModel().getSelectedItems() ObservableList . Thus, basically you cannot change the selection when changing the selection.

In any case, your decision is a bit cumbersome. If you had another listener on the selected item (or combo box), even if your method worked, it will temporarily see the combo box with the "illegal" choice. For example, in the example above, if the user tries to select "1", another listener will see a change in choice for the forbidden value "1", and then back to "0". Processing values โ€‹โ€‹that should not be allowed in this listener is likely to make your program logic pretty complex.

The best approach, imho, is to prohibit the user from selecting forbidden values. You can do this with the factory cell, which sets the disable property of the cell:

  box.setCellFactory(lv -> new ListCell<Integer>() { @Override public void updateItem(Integer item, boolean empty) { super.updateItem(item, empty); if (empty) { setText(null); } else { setText(item.toString()); setDisable(item.intValue() != 0); } } }); 

Inclusion of the following in an external style sheet will give the user a normal visual hint that the elements cannot be selected:

 .combo-box-popup .list-cell:disabled { -fx-opacity: 0.4 ; } 
+8
source

I know the thread is pretty old, but I had a similar problem and worked differently. I tried to change the selected ComboBox element in the onAction method when the element was not available at the moment (for example, due to this condition). As @James_D said in his answer, the problem is setting the object that is being modified.

Just add the code inside the Platform.runLater() method:

Platform.runLater(() -> box.getSelectionModel().select(0));

In my case, it worked, I hope that it will work in others.

+5
source

All Articles