JavaFX ComboBox 更改值导致 IndexOutOfBoundsException
JavaFX ComboBox change value causes IndexOutOfBoundsException
我想包括对我的组合框的检查,以将 "access" 限制为某些值。我可以从列表中删除那些无法访问的项目,是的,但我希望用户看到其他选项,即使他不能 select 他们(还)。
问题:在 changelistener 中选择另一个值会导致 IndexOutOfBoundsException,我不知道为什么。
这是一个 SSCCE。它创建一个具有整数值的 ComboBox,第一个默认为 selected。然后我试图让它变得非常简单:值的每次更改都被视为 "wrong" 并且我将 selection 更改回第一个元素。但是,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$new8(Unknown Source)
at com.sun.javafx.scene.control.behavior.ListViewBehavior$$Lambda6/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.handle(Unknown Source)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase.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.access00(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$handleMouseEvent0(Unknown Source)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$$Lambda2/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$null5(Unknown Source)
at com.sun.glass.ui.win.WinApplication$$Lambda/2117255219.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
我做错了什么?
在 JavaFX 中,您无法在更改正在进行时更改 ObservableList
的内容。这里发生的事情是,作为 box.getSelctionModel().getSelectedItems()
ObservableList
更改的一部分,您的听众(您尝试的任何听众)都被解雇了。所以基本上,在处理 selection 更改时,您不能更改 selection。
无论如何,您的解决方案有点笨拙。如果您在 selected 项目(或组合框值)上有另一个侦听器,即使您的方法有效,它也会暂时看到带有 "illegal" selection 的组合框。例如,在上面的示例中,如果用户尝试 select“1”,则另一个侦听器会看到 selection 更改为不允许的值“1”,然后返回“0”。处理此侦听器中不应允许的值可能会使您的程序逻辑非常复杂。
恕我直言,更好的方法是防止用户 select 使用不允许的值。您可以使用设置单元格 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);
}
}
});
在外部样式中包含以下内容 sheet 会给用户通常的视觉提示,即这些项目不 selectable:
.combo-box-popup .list-cell:disabled {
-fx-opacity: 0.4 ;
}
我知道这个线程很旧,但我遇到了类似的问题,我用不同的方式解决了它。当项目当时不可用时(例如,由于给定的条件),我试图在其 onAction
方法中更改 ComboBox 的选定项目。正如@James_D 在他的回答中所说,问题是设置当前正在修改的对象。
只需在 Platform.runLater()
方法中添加您的代码:
Platform.runLater(() -> box.getSelectionModel().select(0));
在我的案例中有效,希望在其他案例中也有效。
我想包括对我的组合框的检查,以将 "access" 限制为某些值。我可以从列表中删除那些无法访问的项目,是的,但我希望用户看到其他选项,即使他不能 select 他们(还)。
问题:在 changelistener 中选择另一个值会导致 IndexOutOfBoundsException,我不知道为什么。
这是一个 SSCCE。它创建一个具有整数值的 ComboBox,第一个默认为 selected。然后我试图让它变得非常简单:值的每次更改都被视为 "wrong" 并且我将 selection 更改回第一个元素。但是,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$new8(Unknown Source)
at com.sun.javafx.scene.control.behavior.ListViewBehavior$$Lambda6/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.handle(Unknown Source)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase.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.access00(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$handleMouseEvent0(Unknown Source)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$$Lambda2/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$null5(Unknown Source)
at com.sun.glass.ui.win.WinApplication$$Lambda/2117255219.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
我做错了什么?
在 JavaFX 中,您无法在更改正在进行时更改 ObservableList
的内容。这里发生的事情是,作为 box.getSelctionModel().getSelectedItems()
ObservableList
更改的一部分,您的听众(您尝试的任何听众)都被解雇了。所以基本上,在处理 selection 更改时,您不能更改 selection。
无论如何,您的解决方案有点笨拙。如果您在 selected 项目(或组合框值)上有另一个侦听器,即使您的方法有效,它也会暂时看到带有 "illegal" selection 的组合框。例如,在上面的示例中,如果用户尝试 select“1”,则另一个侦听器会看到 selection 更改为不允许的值“1”,然后返回“0”。处理此侦听器中不应允许的值可能会使您的程序逻辑非常复杂。
恕我直言,更好的方法是防止用户 select 使用不允许的值。您可以使用设置单元格 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);
}
}
});
在外部样式中包含以下内容 sheet 会给用户通常的视觉提示,即这些项目不 selectable:
.combo-box-popup .list-cell:disabled {
-fx-opacity: 0.4 ;
}
我知道这个线程很旧,但我遇到了类似的问题,我用不同的方式解决了它。当项目当时不可用时(例如,由于给定的条件),我试图在其 onAction
方法中更改 ComboBox 的选定项目。正如@James_D 在他的回答中所说,问题是设置当前正在修改的对象。
只需在 Platform.runLater()
方法中添加您的代码:
Platform.runLater(() -> box.getSelectionModel().select(0));
在我的案例中有效,希望在其他案例中也有效。