1

在 Visual Basic 6 中,如果将表单按钮控件的 DefaultButton 属性设置为 true,则表单中所有其他按钮控件的相同属性都设置为 false,即该属性是互斥的(就像单选按钮一样)。

我的 GUI 上出现的行为似乎表明 JavaFX 中的多个按钮可能将 defaultButton 属性设置为 true,并且多个按钮将从系统接收 VK_Enter 按钮事件。

setDefaultButton() 方法和 defaultButton 属性的 JavaFX 2.2 文档没有说明此问题。

我有一个带有一堆控件的 AnchorPanel 和另一个带有单独一堆控件的 AnchorPanel。这些都显示在同一个阶段,哪个是 setVisible(true) 给用户取决于他正在使用的信息。

我想知道是否需要遍历活动窗格上的按钮控件并将所有它们的 defaultButton 属性设置为 false,然后我尝试将其设置为 true,以避免由于多个按钮接收 Enter 导致的奇怪行为关键事件。

编辑 2013 年 5 月 5 日——这是我拥有的 ChangeListener 代码。我最初将它编写为一个匿名内部类,我直接将其附加到 TextField 控件 txtDx 的focusedProperty。但是,我认为也许我可以通过在我不希望它触发的时候(即,当它的窗格不可见或禁用时)删除侦听器来修复我的错误。所以我将监听器移动到一个内部类,并有一个实例变量引用它的实例。有了引用,我可以根据显示的窗格添加和删除侦听器。

无论如何,这里是 Listener 类:

private class FocusPropertyChangeListener implements ChangeListener<Boolean> {

    FocusPropertyChangeListener() { System.out.println("New FPCL instance"); }

    @Override
    public void changed(ObservableValue<? extends Boolean> ov, 
        Boolean oldb, Boolean newb) {
        System.out.println("Focus change triggered");

        if (ancEncEditor.isVisible() && !ancEncEditor.isDisabled()) {
            boolean b = (newb != null && newb.booleanValue() == true);
            System.out.println("txtDx focus change event triggered: DxAdd = " + b);

            btnWindowCommit.setDefaultButton(!b);
            btnWindowClose.setCancelButton(true);
            btnDxAdd.setDefaultButton(b);
        }
    }
}

如您所见,在事件处理程序中,我对 setDefaultButton 进行了两次(可能是并发的)调用。如果我在 txtDx 中编辑,目的是使用 btnDxAdd,否则使用 btnWindowCommit。我将尝试摆脱对 setDefaultButton 的可能不需要的调用,看看我是否仍然得到 ConcurrentModificationExceptions。

编辑 05/05/2013 - 添加堆栈跟踪。开始...

java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:894)
at java.util.HashMap$EntryIterator.next(HashMap.java:934)
at java.util.HashMap$EntryIterator.next(HashMap.java:932)
at com.sun.javafx.collections.ObservableMapWrapper$ObservableEntrySet$1.next(ObservableMapWrapper.java:560)
at com.sun.javafx.collections.ObservableMapWrapper$ObservableEntrySet$1.next(ObservableMapWrapper.java:548)
at com.sun.javafx.scene.KeyboardShortcutsHandler.processAccelerators(KeyboardShortcutsHandler.java:286)
at com.sun.javafx.scene.KeyboardShortcutsHandler.dispatchBubblingEvent(KeyboardShortcutsHandler.java:119)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:28)
at javafx.event.Event.fireEvent(Event.java:171)
at javafx.scene.Scene$KeyHandler.process(Scene.java:3513)
at javafx.scene.Scene$KeyHandler.access$2300(Scene.java:3472)
at javafx.scene.Scene.impl_processKeyEvent(Scene.java:1904)
at javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:2270)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:136)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:100)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleKeyEvent(GlassViewEventHandler.java:163)
at com.sun.glass.ui.View.handleKeyEvent(View.java:518)
at com.sun.glass.ui.View.notifyKey(View.java:951)
at com.sun.glass.ui.win.WinApplication._enterNestedEventLoop(Native Method)
at com.sun.glass.ui.Application.enterNestedEventLoop(Application.java:383)
at com.sun.glass.ui.EventLoop.enter(EventLoop.java:83)
at com.sun.javafx.tk.quantum.QuantumToolkit.enterNestedEventLoop(QuantumToolkit.java:520)
at javafx.stage.Stage.showAndWait(Stage.java:397)
at org.kls.md.censusassistant.DialogController.showAndWait(DialogController.java:94)
at org.kls.md.censusassistant.DCMainEditor.handleEncDetails(DCMainEditor.java:287)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:75)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:279)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1435)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:69)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:217)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:170)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:28)
at javafx.event.Event.fireEvent(Event.java:171)
at javafx.scene.Node.fireEvent(Node.java:6863)
at javafx.scene.control.Button.fire(Button.java:179)
at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:193)
at com.sun.javafx.scene.control.skin.SkinBase$4.handle(SkinBase.java:336)
at com.sun.javafx.scene.control.skin.SkinBase$4.handle(SkinBase.java:329)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:64)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:217)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:170)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:33)
at javafx.event.Event.fireEvent(Event.java:171)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3328)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3168)
at javafx.scene.Scene$MouseHandler.access$1900(Scene.java:3123)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1563)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2265)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:250)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:173)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:292)
at com.sun.glass.ui.View.handleMouseEvent(View.java:528)
at com.sun.glass.ui.View.notifyMouse(View.java:922)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.access$100(WinApplication.java:29)
at com.sun.glass.ui.win.WinApplication$3$1.run(WinApplication.java:73)
at java.lang.Thread.run(Thread.java:722)

编辑 2013 年 5 月 5 日——我更改了我的代码,以便不再清除以前的 defaultButton 设置,即只调用一次 setDefaultButton(true)。这个想法是,由于支持代码使用 Runnable 执行此操作,-也许-这是我得到的 ConcurrentModificationExceptions 的原因。

因此,现在看来,当我尝试在 btnDxAdd 和 btnWindowCommit 作为默认按钮之间进行更改时,实际上我不再能够在我的代码中触发 ConcurrentModificationExceptions。

然而 ...

我也不再能够得到我想要的行为。当我现在在 txtDx 控件中进行编辑时,我可以整天按 Enter 键,并且按钮不会触发。我已确认我的 focusProperty ChangeListener 已触发,并且已对 txtDx.setDefaultButton(true) 进行了一次调用。无论如何,我无法获得我想要的行为。

4

1 回答 1

2

关于 javadoc 澄清的错误:javafx-jira.kenai.com/browse/RT-30200

关于实施的错误:https ://javafx-jira.kenai.com/browse/RT-30206

我观看了皮肤类的代码,可以从 OpenJFX 观察到:

发生了什么,当你设置默认(真/假)时:

Runnable defaultButtonRunnable = new Runnable() {
        public void run() {
            if (!getSkinnable().isDisabled()) {
                getSkinnable().fire();
            }
        }
    };

private void setDefaultButton(boolean value) {

    KeyCode acceleratorCode = KeyCode.ENTER;
    defaultAcceleratorKeyCodeCombination = 
            new KeyCodeCombination(acceleratorCode);

    if (! value) {
        /*
        ** first check of there's a default button already
        */
        Runnable oldDefault = getSkinnable().getParent().getScene().getAccelerators().get(defaultAcceleratorKeyCodeCombination);
        if (!defaultButtonRunnable.equals(oldDefault)) {
            /*
            ** is it us?
            */
            getSkinnable().getParent().getScene().getAccelerators().remove(defaultAcceleratorKeyCodeCombination);
        }
    }
    getSkinnable().getParent().getScene().getAccelerators().put(defaultAcceleratorKeyCodeCombination, defaultButtonRunnable);
}

工作原理:当您将新按钮设置为默认按钮时,它会找到现有的默认按钮,并从存储在场景中的加速器列表中删除按 ENTER 键的加速器。并将自身添加为默认按钮。所以你不需要在其他按钮上设置默认值。

于 2013-05-05T15:42:40.017 回答