JavaFX中Fire Button的onAction与Enter键

12

我是JavaFx的新手。在我的JavaFX应用程序中,我设置了onAction属性,并且当我使用鼠标点击按钮时,它可以正常工作。我希望在用户按下Enter键时触发相同的事件。我知道可以使用事件处理程序来实现。但是,当我阅读onAction JavaDoc时,它说此事件通过按键触发。

属性说明:

按钮的操作,在任何时候调用,当用户使用鼠标单击按钮时或者由于触摸事件或按键触发或者如果开发者编程调用fire()方法,则会触发此操作。

但是当我按下Enter键时,什么也没有发生。文档是否有误?是否有其他方法可以在不向按钮添加监听器的情况下实现这一点?

P.S

在评论中建议我使用空格键来测试,然后它就会被触发。但我想将其设置为Enter键。我有很多按钮。我尝试过button.setDefaultButton(true);,但它不起作用。我认为这是因为有多个按钮。如何将其设置为多个按钮?


当您按下键时,该按钮是否具有焦点?在Windows 7上,只有在按下空格键时才会触发操作事件。 - Vertex
是的。在按下键之前,我将焦点设置到按钮上。 - Thusitha Thilina Dayaratne
2
当您按下空格键而不是回车键时,处理程序是否被调用? - Vertex
2
请参见Button.defaultButtonProperty() - Uluk Biy
1
@ThusithaThilinaDayaratne,我有一个想法,你应该测试一下。btn.defaultButtonProperty().bind(btn.focusedProperty()) - Uluk Biy
显示剩余4条评论
3个回答

9
您可以通过绑定,动态更改当前聚焦按钮的默认按钮属性。
btn.defaultButtonProperty().bind(btn.focusedProperty());

0
如果您想将此应用于程序中的每个按钮,可以对JavaFX-Button进行子类化并在构造函数中绑定它。在您的fxml文件中,您需要包含自定义按钮。
我编写了以下子类:
public class FocusedButton extends javafx.scene.control.Button {

    public FocusedButton ( ) {
        super ( );
        bindFocusToDefault ( );
    }

    public FocusedButton ( String text ) {
        super ( text );
        bindFocusToDefault ( );
    }

    public FocusedButton ( String text, Node graphic ) {
        super ( text, graphic );
        bindFocusToDefault ( );
    }

    private void bindFocusToDefault ( ) {
        defaultButtonProperty().bind(focusedProperty());
    }

}

要使用此代码,您需要在fxml文件中包含自定义类:

<?import your-package.*?>

如果您想使用Scene Builder,情况会稍微复杂一些:您需要将自定义按钮导出到jar文件中,并按照此处所述将其添加到Scene Builder中。


0
为了覆盖Enter键的按下行为,我使用以下函数,在场景的按键事件过滤器中调用它:
public static void overrideEnterKeyPressEvent(KeyEvent evt) {
    EventTarget eventTarget = evt.getTarget();
    
    if ((eventTarget instanceof TextArea) || (eventTarget instanceof TableView)) {
        return;
    }
    
    if (eventTarget instanceof Button) {
        Platform.runLater(() -> {
            KeyEvent newEventPressed = new KeyEvent(KeyEvent.KEY_PRESSED, " ", " ", KeyCode.SPACE, false, false, false, false);
            Event.fireEvent(eventTarget, newEventPressed);
            KeyEvent newEventReleased = new KeyEvent(KeyEvent.KEY_RELEASED, " ", " ", KeyCode.SPACE, false, false, false, false);
            Event.fireEvent(eventTarget, newEventReleased);
        });
        evt.consume();
        return;
    }
    
    Platform.runLater(() -> {
        KeyEvent tabEvent = new KeyEvent(KeyEvent.KEY_PRESSED, "", "\t", KeyCode.TAB, evt.isShiftDown(), false, false, false);
        Event.fireEvent(eventTarget, tabEvent);
    });
    evt.consume();
}

根据事件的目标,该函数的工作方式如下。对于TextArea或TableView,它是一个NoOp。对于按钮,它会消耗Enter按下事件并触发Space键按下和释放事件。对于所有其他控件,它也会消耗Enter按下事件并触发Tab事件,因此按Enter键会将焦点移动到下一个控件,就像按Tab键一样。

然后您只需为整个场景注册一个事件过滤器:

    scene.addEventFilter(KeyEvent.KEY_PRESSED, this::onSceneKeyPressedFilter);

事件过滤器看起来像这样:

private void onSceneKeyPressedFilter(KeyEvent evt) {
    switch (evt.getCode()) {
        case ENTER:
            if (evt.isControlDown() && FxTools.isAncestorNodeTargeted(evt.getTarget(), fxHBoxInputAp)) {
                return; //let the events for the fxHBoxInputAp pane pass through
            }
            overrideEnterKeyPressEvent(evt);
            break;
        ...
        default:
            break;
    }
}

----- 因为我忘记包含isAncestorNodeTargeted()函数而进行编辑;感谢您的评论,Robert -----

public static boolean isDescendantOf(Node node, Node ancestor) {
    while ((node != null) && (node != ancestor)) {
        node = node.getParent();
    }
    return (node != null);
}


public static boolean isAncestorNodeTargeted(EventTarget target, Node ancestor) {
    return (target instanceof Node) ? isDescendantOf((Node) target, ancestor) : false;
}

你在使用什么依赖项来调用FxTools.isAncestorNodeTargeted()函数? - Robert Smit
抱歉,是我的错。我忘记包含一个简单的函数了。也许在新版本的JavaFx中有更好的方法,但我不知道。我已经编辑了答案。 - tomorrow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接