【问题标题】:JavaFX 2 -- Setting the defaultButton property: mutually exclusive?JavaFX 2 -- 设置 defaultButton 属性:互斥?
【发布时间】:2013-04-28 20:44:00
【问题描述】:

在 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 Key 事件。

EDIT 05/05/2013 -- 这是我拥有的 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。

EDIT 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)

EDIT 05/05/2013 -- 我更改了我的代码,这样我就不会再清除以前的 defaultButton 设置,即只调用一次 setDefaultButton(true)。这个想法是,由于支持代码使用 Runnable 执行此操作,-也许-这是我得到的 ConcurrentModificationExceptions 的原因。

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

然而……

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

【问题讨论】:

标签: user-interface javafx-2 javafx


【解决方案1】:

关于 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 键的加速器。并将自身添加为默认按钮。所以你不需要在其他按钮上设置默认值。

【讨论】:

  • 感谢您分享您的研究。每当我尝试在我的应用程序中运行更改 setDefaultButton() 和 setCancelButton() 值以响应附加到 TextField 的 FocusedProperty 的 ChangeListener() 上的事件时,我都会收到 ConcurrentModificationExceptions。由于我没有并发,没有迭代集合,甚至在我的应用程序中没有生成异常的 HashTable,我必须得出结论,JavaFX 在某种程度上存在错误。我现在不得不从我的应用程序中删除此代码(没有 ChangeListener 可以正常工作)。这就是我非常讨厌 UI 设计的原因。
  • 看来,这个修改异常是由某种对集合的迭代引起的,我们试图从中删除。
  • 我无法使用 1 个按钮、1 个文本字段和对更改侦听器中的默认值进行操作来重现该问题。您能否附上一段代码,或就此提出问题?我用的是 8.0b87
  • 同意。这不是我可以控制的,所以我无法修复它......我浪费了我生命中的几个小时来确保我没有收到可重入事件调用,并且错误与在禁用或不可见控件上设置属性无关.我将尝试将我的代码更改为只执行一个 setDefaultButton(true) 而不是首先尝试将其他代码重置为 false .... 根据您的研究,我可能通过创建多个属性设置线程而导致错误在 API 中。不过,我认为情况并非如此。
  • 我很乐意提供一些代码。我的应用程序使用一个带有两个窗格的阶段。显示的窗格取决于上下文,并且每个窗格都有自己的一组默认按钮。但是,在一个窗格中有一个 TextField,我希望在编辑时使用不同的 defaultButton(窗格的其余部分使用它的基本 defaultButtons);因此是 FocusedProperty 事件。如果我能想办法用一些代码简洁地展示设计,我会发布它。
猜你喜欢
  • 1970-01-01
  • 2018-11-25
  • 1970-01-01
  • 1970-01-01
  • 2020-03-24
  • 1970-01-01
  • 2021-10-30
  • 1970-01-01
  • 2012-12-17
相关资源
最近更新 更多