JavaFX如何取消ComboBox选择更改? [英] JavaFX how to cancel ComboBox selection change?

查看:222
本文介绍了JavaFX如何取消ComboBox选择更改?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下是演示代码:

public class ComboBoxTest extends Application {

    public static void main(String[] args) {
        Application.launch(ComboBoxTest.class);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        ComboBox<String> comboBox = new ComboBox<>();
        comboBox.getItems().addAll("option1", "option2", "option3");
        comboBox.getSelectionModel().select(0);

        comboBox.getSelectionModel().selectedItemProperty().addListener(
            (_ob, _old, _new) -> {
                if (!isValidChange(_old, _new)) {
                    // ERROR: try to cancel change and get StackOverflowError
                    comboBox.getSelectionModel().select(_old);
                }
        });

        primaryStage.setScene(new Scene(
                new BorderPane(comboBox), 300, 200));
        primaryStage.show();
    }

    private static boolean isValidChange(String _old, String _new) {
        // return false;
        return !_old.equals("option1");
    }
}

更改 comboBox的选择时,它只会引发 StackOverFlowError ,我知道原因( ChangeListener 对象被触发一次又一次),但我不知道如何正确取消此选择更改操作。

When changing selection of comboBox, it centainly throws StackOverFlowError, I know the reason why (the ChangeListener object is triggered again and again), but what I don't know is how to correctly "cancel" this selection change operation.

更新:

抱歉@fabian我在这里写了一个不好的例子,让我稍微改变一下:

Sorry @fabian I wrote a bad example here, let me change it a little bit:

private static boolean isValidChange(String _old, String _new) {
    return !_old.equals("option1");
}

现在可以使用, comboBox 值无法更改。但是我在这里得到一个奇怪的 IndexOutOfBoundsException ,而堆栈跟踪中没有我的代码行:

It works now, the comboBox value cannot be changed. However I get a strange IndexOutOfBoundsException here, while there is no line of my code in the stack trace:

Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException
    at com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList.subList(ReadOnlyUnbackedObservableList.java:136)
    at javafx.collections.ListChangeListener$Change.getAddedSubList(ListChangeListener.java:242)
    at com.sun.javafx.scene.control.behavior.ListViewBehavior.lambda$new$177(ListViewBehavior.java:269)
    at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
    at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)
    at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
    at com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList.callObservers(ReadOnlyUnbackedObservableList.java:75)
    at javafx.scene.control.MultipleSelectionModelBase.clearAndSelect(MultipleSelectionModelBase.java:378)
    at javafx.scene.control.ListView$ListViewBitSetSelectionModel.clearAndSelect(ListView.java:1403)
    at com.sun.javafx.scene.control.behavior.CellBehaviorBase.simpleSelect(CellBehaviorBase.java:256)
    at com.sun.javafx.scene.control.behavior.CellBehaviorBase.doSelect(CellBehaviorBase.java:220)
    at com.sun.javafx.scene.control.behavior.CellBehaviorBase.mousePressed(CellBehaviorBase.java:150)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:95)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
    at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:380)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:294)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$354(GlassViewEventHandler.java:416)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:415)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
    at com.sun.glass.ui.View.notifyMouse(View.java:937)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
    at java.lang.Thread.run(Thread.java:745)

这是JavaFX的错误吗?

Is this a bug of JavaFX?

推荐答案

这里有2个问题。


  1. 从值 a 更改为值 b 是无效,但从 b a 的更改无效。因此,当值更改时,侦听器撤消更改,这会导致侦听器尝试撤消的另一个更改事件,侦听器再次尝试撤消,...

  1. A change from value a to value b is not valid, but also a change from b to a is invalid. Therefore when the value is changed the listener undos the change which leads to another change event that the listener tries to undo, which again the listener tries to undo, ...

您可以修改选择模型。但是,选择模型依赖于选择不变。

You modify the selection model. However the selection model relies on the selection being unchanged.

要解决这些问题,请引入 ChangeListener 知道它何时否决更改并在使用 Platform.runLater 处理更改事件后运行更改:

To fix these issues introduce a ChangeListener that "knows when it's vetoing a change" and runs the change after the change event was handled using Platform.runLater:

public abstract class VetoListener<T> implements ChangeListener<T> {

    private final SelectionModel<T> selectionModel;
    private boolean changing = false;

    public VetoListener(SelectionModel<T> selectionModel) {
        if (selectionModel == null) {
            throw new IllegalArgumentException();
        }
        this.selectionModel = selectionModel;
    }

    @Override
    public void changed(ObservableValue<? extends T> observable, T oldValue, T newValue) {
        if (!changing && isInvalidChange(oldValue, newValue)) {
            changing = true;
            Platform.runLater(() -> {
                selectionModel.select(oldValue);
                changing = false;
            });
        }
    }

    protected abstract boolean isInvalidChange(T oldValue, T newValue);

}

以下代码将阻止来自A到B,从BC

The following code will prevent changes from "A" to "B" and from "B" to "C":

@Override
public void start(Stage primaryStage) {
    ComboBox<String> combo = new ComboBox<>(FXCollections.observableArrayList("A", "B", "C"));

    combo.getSelectionModel().selectedItemProperty().addListener(new VetoListener<String>(combo.getSelectionModel()) {

        @Override
        protected boolean isInvalidChange(String oldValue, String newValue) {
            return oldValue != null && newValue != null
                    && !oldValue.isEmpty()
                    && !newValue.isEmpty()
                    && oldValue.charAt(0)+1 == newValue.charAt(0);
        }

    });

    Scene scene = new Scene(combo);

    primaryStage.setScene(scene);
    primaryStage.show();
}

这篇关于JavaFX如何取消ComboBox选择更改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆