当前位置: 首页 > 知识库问答 >
问题:

是否可以从 JavaFX FileChooser 窗口中使用该事件?

姬正文
2023-03-14

我有一个JavaFX按钮,当用户按Enter时触发。这会导致FileChooser打开。有些人(像我一样)可能会在FileChooser中点击Enter来保存文件。但是,这会导致保存按钮再次触发自身并再次打开FileChooser以保存新文件。用鼠标单击按钮(在FileChooser中)没有这个问题。

我以为从按钮中使用事件会解决这个问题,但它只使用GUI事件上的按钮,而不是FileChooser按钮。我尝试过寻找修改FileChooser的EventHandler以使用回车键的方法,但没有成功。
我还尝试过将焦点从按钮上移开并将其移动到父级(窗格),以便无法再次单击它。但是,有些按钮可以多次单击而无需再次重新关注它们。

我的代码示例如下所示(显然这将是扩展Application的更大类的一部分):

EventHandler<KeyEvent> enter = event -> {
    if (event.getCode() == KeyCode.ENTER && event.getSource() instanceof Button) {
        Button src = (Button) event.getSource();
        src.fire();
    }
    event.consume();
};

Button b1 = new Button("Save");

b1.setOnKeyReleased(enter);

/* Called by .fire method */
b1.setOnAction(event -> {
    /* Create the save dialog box */
    FileChooser saveDialog = new FileChooser();
    saveDialog.setTitle("Save");

    /* Get file */
    File f = saveDialog.showSaveDialog(stage);
    /*
     * ... do stuff with file ...
     */
});

注意:这个例子不是我的确切代码。相反,密钥释放事件是用于多个按钮的变量,而不仅仅是保存按钮(即b2.setOnKeyReleased(Enter);b2.setOnAction(事件-

如何防止在用户在文件选择器中按 Enter 键时触发按钮?我不希望用户在没有鼠标的情况下陷入循环。我知道按Alt S也可以保存它,但我不能指望所有用户都知道这一点。

编辑:根据现在似乎已被删除的评论中的要求,以下是代码的可运行版本:

import java.io.File;

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

public class ButtonTest extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        /* EventHandler to be used with multiple buttons */
        EventHandler<KeyEvent> enter = event -> {
            if (event.getCode() == KeyCode.ENTER && event.getSource() instanceof Button) {
                Button src = (Button) event.getSource();
                src.fire();
            }
            event.consume();
        };

        /* Create a new button */
        Button b1 = new Button("Save");
        Button b2 = new Button("Print");
        /* Add event handlers */
        b1.setOnKeyReleased(enter);
        b2.setOnKeyReleased(enter);

        /* Called by .fire method of save button */
        b1.setOnAction(event -> {
            /* Create the save dialog box */
            FileChooser saveDialog = new FileChooser();
            saveDialog.setTitle("Save");

            /* Get file */
            File f = saveDialog.showSaveDialog(stage);
            /* ... do stuff with file ... */
        });
        /* Called by .fire method of print button */
        b2.setOnAction(event -> System.out.println("Pressed"));

        Scene scene = new Scene(new HBox(b1, b2));
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

共有2个答案

费辰阳
2023-03-14

我为文件添加了一个布尔值Chooser正在打开,它似乎对我有用,但我不得不拆分事件,否则它只会每隔一次触发打印按钮

public class Main extends Application {

    private boolean fileChooserOpen = false;

    @Override
    public void start(Stage stage) throws Exception{
        /* EventHandler to be used with multiple buttons */
        EventHandler<KeyEvent> enterWithFileChooser = event -> {
            if (!fileChooserOpen && event.getCode() == KeyCode.ENTER && event.getSource() instanceof Button) {
                Button src = (Button) event.getSource();
                src.fire();
                fileChooserOpen = true;
            }else {
                fileChooserOpen = false;
            }
            event.consume();
        };

        EventHandler<KeyEvent> enter = event -> {
            if (event.getCode() == KeyCode.ENTER && event.getSource() instanceof Button) {
                Button src = (Button) event.getSource();
                src.fire();
            }
            event.consume();
        };

        /* Create a new button */
        Button b1 = new Button("Save");
        Button b2 = new Button("Print");
        /* Add event handlers */
        b1.setOnKeyReleased(enterWithFileChooser);
        b2.setOnKeyReleased(enter);

        /* Called by .fire method of save button */
        b1.setOnAction(event -> {
            /* Create the save dialog box */
            FileChooser saveDialog = new FileChooser();
            saveDialog.setTitle("Save");

            /* Get file */
            File f = saveDialog.showSaveDialog(stage);
            /* ... do stuff with file ... */
        });
        /* Called by .fire method of print button */
        b2.setOnAction(event -> System.out.println("Pressed"));

        Scene scene = new Scene(new HBox(b1, b2));
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) { launch(args); }
}
陆琦
2023-03-14
匿名用户

问题是从< code>onKeyReleased处理程序中触发< code >按钮。当您释放< kbd>ENTER键时,< code>FileChooser已被隐藏,并且< code>Stage已重新获得焦点,这意味着键释放事件被给予您的< code>Stage/按钮。显然这会造成一个循环。

一种可能的解决方案是从onKeyPress处理程序内部触发Button。但是,这将提供与其他应用程序略有不同的行为,这是您的用户可能不期望/欣赏的。

另一种可能的解决方案是跟踪FileChooser在触发Button之前是否已打开,就像Matt在他的答案中所做的那样。

您似乎试图做的是允许用户使用ENTER键来触发Button;这应该是Windows等平台上的默认行为。

不适合我。空格是触发按钮的唯一键。我认为这是因为回车键用于触发默认按钮,该按钮使用btn.setDefaultButton(true)设置;

对我来说,在Windows 10上使用JavaFX 11.0.2而不是JavaFX 8u202时,当< code >按钮具有焦点时按< kbd>ENTER会触发action事件。从JavaFX 8开始,< code >按钮的行为似乎发生了变化。下面是< code > com . sun . Java FX . scene . control . behavior . button behavior 的不同实现,显示了注册的键绑定。

JavaFX 8u202

protected static final List<KeyBinding> BUTTON_BINDINGS = new ArrayList<KeyBinding>();
static {
        BUTTON_BINDINGS.add(new KeyBinding(SPACE, KEY_PRESSED, PRESS_ACTION));
        BUTTON_BINDINGS.add(new KeyBinding(SPACE, KEY_RELEASED, RELEASE_ACTION));
}

JavaFX 11.0.2

public ButtonBehavior(C control) {
    super(control);

    /* SOME CODE OMITTED FOR BREVITY */

    // then button-specific mappings for key and mouse input
    addDefaultMapping(buttonInputMap,
        new KeyMapping(SPACE, KeyEvent.KEY_PRESSED, this::keyPressed),
        new KeyMapping(SPACE, KeyEvent.KEY_RELEASED, this::keyReleased),
        new MouseMapping(MouseEvent.MOUSE_PRESSED, this::mousePressed),
        new MouseMapping(MouseEvent.MOUSE_RELEASED, this::mouseReleased),
        new MouseMapping(MouseEvent.MOUSE_ENTERED, this::mouseEntered),
        new MouseMapping(MouseEvent.MOUSE_EXITED, this::mouseExited),

        // on non-Mac OS platforms, we support pressing the ENTER key to activate the button
        new KeyMapping(new KeyBinding(ENTER, KeyEvent.KEY_PRESSED), this::keyPressed, event -> PlatformUtil.isMac()),
        new KeyMapping(new KeyBinding(ENTER, KeyEvent.KEY_RELEASED), this::keyReleased, event -> PlatformUtil.isMac())
    );

    /* SOME CODE OMITTED FOR BREVITY */

}

如您所见,两者都注册SPACE以在按钮具有焦点时触发它。但是,JavaFX 11.0.2 实现也为相同的平台注册 ENTER,但仅适用于非 Mac OS 平台。我找不到有关此行为更改的任何文档。

如果您希望JavaFX 8具有相同的行为,并且不介意深入JavaFX的内部,那么您可以使用反射来改变应用程序中所有类似按钮的控件的行为。下面是一个实用方法示例:

import com.sun.javafx.PlatformUtil;
import com.sun.javafx.scene.control.behavior.ButtonBehavior;
import com.sun.javafx.scene.control.behavior.KeyBinding;
import java.lang.reflect.Field;
import java.util.List;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

public final class ButtonUtils {

  public static void installEnterFiresButtonFix() throws ReflectiveOperationException {
    if (PlatformUtil.isMac()) {
      return;
    }

    Field bindingsField = ButtonBehavior.class.getDeclaredField("BUTTON_BINDINGS");
    Field pressedActionField = ButtonBehavior.class.getDeclaredField("PRESS_ACTION");
    Field releasedActionField = ButtonBehavior.class.getDeclaredField("RELEASE_ACTION");

    bindingsField.setAccessible(true);
    pressedActionField.setAccessible(true);
    releasedActionField.setAccessible(true);

    @SuppressWarnings("unchecked")
    List<KeyBinding> bindings = (List<KeyBinding>) bindingsField.get(null);
    String pressedAction = (String) pressedActionField.get(null);
    String releasedAction = (String) releasedActionField.get(null);

    bindings.add(new KeyBinding(KeyCode.ENTER, KeyEvent.KEY_PRESSED, pressedAction));
    bindings.add(new KeyBinding(KeyCode.ENTER, KeyEvent.KEY_RELEASED, releasedAction));
  }

  private ButtonUtils() {}

}

您应该在应用程序启动的早期,在创建任何< code >按钮之前调用此实用工具方法。下面是一个使用它的例子:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

public class Main extends Application {

  @Override
  public void start(Stage primaryStage) {
    try {
      ButtonUtils.installEnterFiresButtonFix();
    } catch (ReflectiveOperationException ex) {
      ex.printStackTrace();
    }
    Button button = new Button("Save");
    button.setOnAction(event -> {
      event.consume();
      System.out.println(new FileChooser().showSaveDialog(primaryStage));
    });
    Scene scene = new Scene(new StackPane(button), 300, 150);
    primaryStage.setScene(scene);
    primaryStage.setTitle("Workshop");
    primaryStage.show();
  }

}

提醒:此修复依赖于实现。

 类似资料:
  • AccessibilityService在系统的内存管理方面与普通服务有何不同? 我想问的是:系统可以为了节省内存而关闭AccessibilityService吗?如果可以,当您进入AccessibilityService时,您会看到它打开还是关闭?用户是否必须再次手动打开? 使用startForeground是否有助于使AccessibilityService保持活动状态? 在一个类似的问题中,

  • 我想拆分我的验证器的声明和实现,与Spring boot环境中的这个问题非常相似。看起来好像是我让它几乎起作用了。我看到我的验证器实际上是由Spring验证调用的,但在执行验证后,Hibernate会抛出一个异常: 这是因为是一个接口(如预期)。 我已经这样配置了Spring(这是一个不同问题的答案): 我的自定义验证器: 所以它试图通过验证器名称找到一个Spring bean。所以我有一个验证器

  • 目前,不允许在接口中使用get/set方法(访问器)。例如: 此外,TypeScript 不允许在类方法中使用数组函数表达式:例如: 有没有其他方法可以在接口定义上使用getter和setter?

  • 问题内容: 我正在尝试使用jQuery在特定端口上运行AJAX查询: 这不起作用:没有进行AJAX调用,并且Firebug中没有任何异常。如果我不指定端口,它确实可以工作。有人知道为什么吗? 问题答案: 由于“ 同源来源”政策而无法使用。仅在相同的域,协议和端口中才允许AJAX请求。 如果您确实需要从该来源获取数据,则应该期待JSONP。

  • 是否可以在 SQL SERVER 中的事务中使用 SELECT 语句锁定行?我想锁定该行,以便外部的其他事务无法访问该行。 提交或回滚事务后,应释放该行。这就是我的意思。。。 有人有建议吗?我应该执行 UPDATE 语句来锁定该行吗? 请不要将此问题标记为重复问题。因为,我不是在问UPDATE语句,而是在问SELECT 编辑:我试图“设置事务隔离级别串行化”,但这锁定了太多东西。我的SP很大,它有

  • 问题内容: 我不确定如何更清楚地表达我的问题。基本上,PyPlot是否限于一个实例/窗口?我尝试的任何破解或变通办法要么导致程序冻结,要么使第二个pyplot窗口排队,直到第一个关闭。 问题答案: 当然,只需打开一个新图即可: 如果您在默认的python解释器中运行此命令,则此命令将无效,因为每个图形都需要输入gui的mainloop。如果要在交互式shell中运行事物,请查看IPython。但是