Events 是否可以从JavaFXFileChooser窗口使用事件?

Events 是否可以从JavaFXFileChooser窗口使用事件?,events,button,javafx,event-handling,filechooser,Events,Button,Javafx,Event Handling,Filechooser,我有一个JavaFX按钮,当用户按下enter键时触发。这会导致文件选择器打开。有些人(比如我)可能会在文件选择器中按enter键来保存文件。但是,这会导致“保存”按钮再次触发,并再次打开文件选择器以保存新文件。用鼠标单击按钮(在文件选择器中)没有此问题 我认为从按钮中使用事件可以解决这个问题,但它只使用GUI事件上的按钮,而不是FileChooser按钮。我试图寻找方法修改FileChooser的EventHandler以使用enter键,但没有成功。 我还尝试过将焦点从按钮上移开,并将其移动

我有一个JavaFX按钮,当用户按下enter键时触发。这会导致文件选择器打开。有些人(比如我)可能会在文件选择器中按enter键来保存文件。但是,这会导致“保存”按钮再次触发,并再次打开文件选择器以保存新文件。用鼠标单击按钮(在文件选择器中)没有此问题

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

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


我为打开的文件选择器添加了一个布尔值,它似乎对我有效,但我必须将事件拆分,否则它只会每隔一次触发打印按钮

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); }
}
public类主扩展应用程序{
private boolean fileChooserOpen=false;
@凌驾
public void start(Stage)引发异常{
/*与多个按钮一起使用的EventHandler*/
EventHandler enterWithFileChooser=事件->{
如果(!filechooseOpen&&event.getCode()==KeyCode.ENTER&&event.getSource()instanceof按钮){
Button src=(Button)event.getSource();
src.fire();
fileChooserOpen=true;
}否则{
fileChooserOpen=false;
}
event.consume();
};
EventHandler enter=事件->{
if(event.getCode()==KeyCode.ENTER&&event.getSource()instanceof按钮){
Button src=(Button)event.getSource();
src.fire();
}
event.consume();
};
/*创建一个新按钮*/
按钮b1=新按钮(“保存”);
按钮b2=新按钮(“打印”);
/*添加事件处理程序*/
b1.setOnKeyReleased(使用文件选择器输入);
b2.setOnKeyReleased(输入);
/*保存按钮的.fire方法调用*/
b1.设置动作(事件->{
/*创建“保存”对话框*/
FileChooser saveDialog=新建FileChooser();
saveDialog.setTitle(“保存”);
/*获取文件*/
文件f=saveDialog.showsavedilog(stage);
/*…处理文件*/
});
/*打印按钮的.fire方法调用*/
b2.设置操作(事件->系统输出打印项次(“按下”);
场景=新场景(新HBox(b1,b2));
舞台场景;
stage.show();
}
公共静态void main(字符串[]args){launch(args);}
}

我为打开的文件选择器添加了一个布尔值,它似乎对我有效,但我必须将事件拆分,否则它只会每隔一段时间触发打印按钮

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); }
}
public类主扩展应用程序{
private boolean fileChooserOpen=false;
@凌驾
public void start(Stage)引发异常{
/*与多个按钮一起使用的EventHandler*/
EventHandler enterWithFileChooser=事件->{
如果(!filechooseOpen&&event.getCode()==KeyCode.ENTER&&event.getSource()instanceof按钮){
Button src=(Button)event.getSource();
src.fire();
fileChooserOpen=true;
}否则{
fileChooserOpen=false;
}
event.consume();
};
EventHandler enter=事件->{
if(event.getCode()==KeyCode.ENTER&&event.getSource()instanceof按钮){
Button src=(Button)event.getSource();
src.fire();
}
event.consume();
};
/*创建一个新按钮*/
按钮b1=新按钮(“保存”);
按钮b2=新按钮(“打印”);
/*添加事件处理程序*/
b1.setOnKeyReleased(使用文件选择器输入);
b2.setOnKeyReleased(输入);
/*保存按钮的.fire方法调用*/
b1.设置动作(事件->{
/*创建“保存”对话框*/
FileChooser saveDialog=新建FileChooser();
saveDialog.setTitle(“保存”);
/*获取文件*/
文件f=saveDialog.showsavedilog(stage);
/*…处理文件*/
});
/*打印按钮的.fire方法调用*/
b2.设置操作(事件->系统输出打印项次(“按下”);
场景=新场景(新HBox(b1,b2));
舞台场景;
stage.show();
}
公共静态void main(字符串[]args){launch(args);}
}

问题是从
onkeyreased
处理程序中启动
按钮。当您松开ENTER键时,
文件选择器
已被隐藏,
阶段
已恢复焦点,这意味着按键释放事件将被赋予您的
阶段
/
按钮
。显然,这将导致一个循环

一种可能的解决方案是从按下
键的
处理程序内部启动
按钮。但是,这会使您的用户产生与其他应用程序稍有不同的行为,而您的用户可能并不期望/欣赏这些行为

另一种可能的解决方案是跟踪
文件选择器
在启动
按钮之前是否已打开,就像Matt在中所做的那样

您试图做的是允许用户使用ENTER键启动
按钮
;这应该是Windows等平台上的默认行为

不适合我。空间是o
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); }
}
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));
}
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 */

}
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() {}

}
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();
  }

}