Значение изменения JavaFX ComboBox вызывает исключение IndexOutOfBoundsException
Я хочу включить проверки для моего комбинированного списка, чтобы ограничить "доступ" к некоторым значениям. Я мог бы просто удалить эти недоступные элементы из списка, да, но я бы хотел, чтобы пользователь видел другие варианты, даже если он не может их выбрать (пока).
Проблема: выбор другого значения в списке изменений вызывает исключение IndexOutOfBoundsException, и я понятия не имею, почему.
Вот SSCCE. Он создает ComboBox со значениями Integer, и первый из них выбирается по умолчанию. Затем я попытался сделать это очень просто: каждое изменение значения считается "неправильным", и я возвращаю выбор к первому элементу. Но все же, 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();
}
}
Я проверил его с помощью valueProperty, selectedItemProperty и selectedIndexProperty, а также всех этих:
box.getSelectionModel().select(0);
box.getSelectionModel().selectFirst();
box.getSelectionModel().selectPrevious();
box.setValue(0);
if (oldValue.intValue() < newValue.intValue())
box.getSelectionModel().select(oldValue.intValue());
Единственная мысль, которая работает, это установка самого значения:
box.getSelectionModel().select(box.getSelectionModel().getSelectedIndex());
box.setValue(box.getValue));
Вот исключение:
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)
Что я делаю неправильно?
2 ответа
В JavaFX вы не можете изменить содержимое ObservableList
в то время как изменение уже происходит. Здесь происходит то, что ваши слушатели (любые из тех, кого вы пытаетесь) увольняют как часть box.getSelctionModel().getSelectedItems()
ObservableList
меняется. Таким образом, в основном, вы не можете изменить выбор, пока изменение выбора обрабатывается.
Ваше решение в любом случае немного громоздко. Если у вас был другой прослушиватель для выбранного элемента (или значение поля со списком), даже если ваш метод работал, он временно увидел бы поле со списком с "недопустимым" выбором. Например, в приведенном выше примере, если пользователь попытается выбрать "1", другой слушатель увидит изменение выбора на запрещенное значение "1", а затем вернется к "0". Работа со значениями, которые не должны быть разрешены в этом слушателе, вероятно, сделает вашу программную логику довольно сложной.
Лучший подход, imho, состоит в том, чтобы запретить пользователю выбирать запрещенные значения. Вы можете сделать это с помощью фабрики ячеек, которая устанавливает disable
свойство ячейки:
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);
}
}
});
Включение следующего во внешнюю таблицу стилей даст пользователю обычный визуальный намек на то, что элементы нельзя выбрать:
.combo-box-popup .list-cell:disabled {
-fx-opacity: 0.4 ;
}
Я знаю, что тема довольно старая, но у меня была похожая проблема, и я решил ее по-другому. Я пытался изменить выбранный элемент ComboBox в его onAction
метод, когда элемент не был доступен в тот момент (например, из-за заданного условия). Как сказал @James_D в своем ответе, проблема заключается в настройке объекта, который в данный момент изменяется.
Просто добавьте свой код внутри Platform.runLater()
метод:
Platform.runLater(() -> box.getSelectionModel().select(0));
В моем случае это сработало, надеюсь, оно будет работать и в других.