我有一个JavaFX Button,当用户按下Enter时触发。这将导致FileChooser打开。有些人(例如我本人)可能会在FileChooser内部打入Enter来保存文件。但是,这导致保存按钮再次触发自身,并再次打开FileChooser来保存新文件。用鼠标单击按钮(在FileChooser中)不会出现此问题。
我认为使用按钮上的事件可以解决此问题,但是它仅使用GUI事件上的按钮,而不使用FileChooser按钮。我尝试寻找修改FileChooser的EventHandler来消耗Enter键的方法,但没有成功。
我还尝试过将焦点从按钮上移开,并将其移到父级(窗格)上,这样就无法再次单击它。但是,有些按钮可以多次被单击,而不必重新集中精力于它们。
我的代码示例如下(显然,这将是扩展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(event -> {/* Do something */});
。
当用户在FileChooser中按下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();
}
}
答案 0 :(得分:2)
问题是从Button
处理程序触发onKeyReleased
。在您释放 ENTER 键时,FileChooser
已被隐藏,Stage
已重新获得焦点,这意味着键释放事件已分配给您的Stage
/ Button
。显然,这将导致一个循环。
一种可能的解决方案是从Button
处理程序内部触发onKeyPressed
。相对于其他应用程序,这将产生稍微不同的行为,但是您的用户可能不会期望/赞赏。
另一种可能的解决方案是,在触发FileChooser
之前跟踪Button
是否已打开,就像Matt在his answer中所做的那样。
您似乎想做的是允许用户使用 ENTER 键触发Button
;在Windows之类的平台上,此应该是默认行为。
不适合我。空格键是触发按钮的唯一键。我认为原因是因为enter用于触发使用
设置的默认按钮btn.setDefaultButton(true);
对我来说,Button
处于焦点状态时按 ENTER 会同时在Windows 10和Windows 10上使用JavaFX 11.0.2而不是JavaFX 8u202时触发操作事件。 {1}}自JavaFX 8以来发生了变化。以下是Button
的不同实现,显示了已注册的键绑定。
JavaFX 8u202
com.sun.javafx.scene.control.behavior.ButtonBehavior
JavaFX 11.0.2
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));
}
如您所见,这两个寄存器都注册了 SPACE 以在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 */
}
具有焦点时触发它们。但是,JavaFX 11.0.2实现也为此注册了 ENTER ,但仅适用于非Mac OS平台。我找不到有关此行为变化的任何文档。
如果您希望JavaFX 8中具有相同的行为,并且不介意侵入JavaFX的内部结构,则可以使用反射来更改所有按钮中的控件的行为应用。这是一个实用程序方法示例:
Button
在创建任何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() {}
}
之前,应在应用程序启动初期调用此实用程序方法。这是一个使用它的示例:
Button
提醒:此修补程序取决于实现。
答案 1 :(得分:1)
我为打开的fileChooser添加了一个布尔值,它似乎对我有用,但是我必须将事件拆分开,否则只会彼此触发打印按钮
'|'.join