检查动作事件中是否按下了“快捷键”键

问题描述 投票:0回答:2

我试图检测在按下“快捷键”时何时触发按钮的操作(可能是通过单击或键盘之类的其他方式)。我找不到从

ActionEvent
按下按键的方法,因此我在场景中放置了按键和释放的事件过滤器,以跟踪按下了哪些按键。然后在按钮的操作中检查是否按下了快捷键。

    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.input.KeyCode;
    import javafx.scene.input.KeyEvent;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    import java.util.HashSet;
    import java.util.Set;
    
    public class App extends Application {
        private final Set<KeyCode> pressedKeys = new HashSet<>();
        
        public static void main(String[] args) {
            launch(args);
        }
        
        @Override
        public void start(Stage stage) {
            Button button = new Button("Click with shortcut key pressed");
            button.setOnAction(e -> {
                if (pressedKeys.contains(KeyCode.SHORTCUT)) {
                    System.out.println("Success!");
                } else {
                    System.out.println("Failure!");
                }
            });
            VBox vBox = new VBox(button);
            vBox.setStyle("-fx-background-color:red;");
            Scene scene = new Scene(vBox);
            scene.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
                System.out.println("Key pressed: " + e.getCode());
                pressedKeys.add(e.getCode());
            });
            scene.addEventFilter(KeyEvent.KEY_RELEASED, e -> {
                System.out.println("Key released: " + e.getCode());
                pressedKeys.remove(e.getCode());
            });
            stage.setScene(scene);
            stage.show();
        }
        
        public static class AppRunner {
            public static void main(String[] args) {
                App.main(args);
            }
        }
    }

但是我发现有两个问题:

  1. 当我按住快捷键并单击按钮时,根本不会触发该操作(不会打印成功或失败)
  2. 如果我将按钮的操作更改为单击事件过滤器,我总是会失败。使用 IntelliJ 的调试器,我看到已知按下了
    KeyCode.CONTROL
    键,但没有按下
    SHORTCUT
    键,即使在本例中它们是相同的键。如何以与平台无关的方式检查按下的任何按键是否为快捷键?
button.addEventFilter(MouseEvent.MOUSE_CLICKED, e -> {
    if (pressedKeys.contains(KeyCode.SHORTCUT)) {
        System.out.println("Success!");
    } else {
        System.out.println("Failure!");
    }
});

我还需要它与其他控件上的操作一起使用,例如组合框和文本字段。

编辑:我发现了这种方法的另一个错误。如果用户按住某个键并切换到另一个场景或应用程序然后释放该键,则该场景将不会检测到该键的释放,并且仍会认为该键被按下。因此,我可以采用一种方法来检测按下了哪些键,而无需跟踪每个按键的按下/释放。 MouseEvents 如何获知哪些键被按下,以便它们可以使用

MouseEvent.isShortcutDown()
?这里可以用同样的方法吗?

编辑:要回答jewelsea的更多背景问题,这有点复杂,但想象一下我有一个带有多种类型控件的表单,可用于在表单中设置值,并且需要多次填写表单。因此,我想要一种方法,不仅可以在表单中设置值,还可以修复它或将其设置为默认值,以便下次需要填写时,它将预先填充该值。快捷键是区分设置值而不固定值和设置值并固定值的便捷方法。这适用于通过快捷方式单击组合框中的按钮或下拉项,或者在文本字段中按 Enter 键的同时按住快捷键。我还认为,如果这个问题的答案适用于任何控件上的动作事件,那么对其他人来说会更有用,但即使让它适用于按钮也会很好。

javafx event-handling keyboard-shortcuts keyevent actionevent
2个回答
0
投票

如果您只需要检查在键盘或鼠标事件期间是否按下了快捷键,那么

event.isShortcutDown()
就足够了。 因为它的实现看起来像这样:

public final boolean isShortcutDown() {
    switch (Toolkit.getToolkit().getPlatformShortcutKey()) {
        case SHIFT:
            return shiftDown;

        case CONTROL:
            return controlDown;

        case ALT:
            return altDown;

        case META:
            return metaDown;

        default:
            return false;
    }
}

即使无法访问

Toolkit.getToolkit().getPlatformShortcutKey()
,也有一种方法可以通过以下方式找出哪个键是
shortcut
键:

event.isControlDown(), event.isShiftDown(), event.isAltDown(), event.isMetaDown()

而且您不必担心切换到另一个场景 - 即使在这种情况下,这些方法也会返回正确的值。

以下应用程序创建两个可以切换的场景(“切换场景”“切换回”按钮)。 第一个包含一些 UI 控件(ButtonTextFieldComboBox)以及打印快捷方式检测结果的

KEY_PRESSED
MOUSE_PRESSED
事件过滤器。 秒场景的目的只是切换(它不捕获快捷事件)。

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextField;
import javafx.scene.input.InputEvent;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class App extends Application {

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

    @Override
    public void start(Stage stage) {
        Button button = new Button("Click with shortcut key pressed");
        TextField text = new TextField("TextField Value");

        ComboBox<String> combo = new ComboBox<>(
                FXCollections.observableArrayList("ComboBox Value 1", "ComboBox Value 2"));
        combo.getSelectionModel().select(0);
        Button switcher = new Button("Switch Scene");

        VBox vBox = new VBox(button, text, combo, switcher);
        vBox.setStyle("-fx-background-color:red;");
        vBox.getChildren().forEach(App::addShortcutCaptureFilters);
        Scene scene = new Scene(vBox);

        Button switcherBack = new Button("Switch Back");
        Scene anotherScene = new Scene(new VBox(new Button("Another Button"), switcherBack));

        switcher.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> stage.setScene(anotherScene));
        switcherBack.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> stage.setScene(scene));

        stage.setScene(scene);
        stage.show();
    }

    private static void addShortcutCaptureFilters(Node control) {

        control.addEventFilter(KeyEvent.KEY_PRESSED, e -> onShortcutCaptureEvent(control, e, e.isShortcutDown(),
                e.isControlDown(), e.isShiftDown(), e.isAltDown(), e.isMetaDown()));

        control.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> onShortcutCaptureEvent(control, e, e.isShortcutDown(),
                e.isControlDown(), e.isShiftDown(), e.isAltDown(), e.isMetaDown()));
    }

    private static void onShortcutCaptureEvent(Node control, InputEvent event, boolean shortcut, boolean ctrl,
            boolean shift, boolean alt, boolean meta) {
        KeyCode specKeyDown = null;
        if (ctrl) {
            specKeyDown = KeyCode.CONTROL;
        } else if (shift) {
            specKeyDown = KeyCode.SHIFT;
        } else if (alt) {
            specKeyDown = KeyCode.ALT;
        } else if (meta) {
            specKeyDown = KeyCode.META;
        }
        String ln = shortcut ? "\n" : "";
        String title = shortcut ? "SHORTCUT DETECTED" : "not a shortcut";

        System.out.println(String.format("%s%s: %s (specKey: %s) %s%s", ln, control.getClass().getSimpleName(), title,
                specKeyDown, event.getEventType(), ln));
    }

    public static class AppRunner {
        public static void main(String[] args) {
            App.main(args);
        }
    }
}

输出示例:

Button: not a shortcut (specKey: null) MOUSE_PRESSED
Button: not a shortcut (specKey: null) MOUSE_PRESSED

Button: SHORTCUT DETECTED (specKey: CONTROL) KEY_PRESSED

TextField: not a shortcut (specKey: null) MOUSE_PRESSED
TextField: not a shortcut (specKey: SHIFT) KEY_PRESSED
TextField: not a shortcut (specKey: SHIFT) MOUSE_PRESSED

TextField: SHORTCUT DETECTED (specKey: CONTROL) KEY_PRESSED

ComboBox: not a shortcut (specKey: ALT) MOUSE_PRESSED
ComboBox: not a shortcut (specKey: null) MOUSE_PRESSED
ComboBox: not a shortcut (specKey: ALT) KEY_PRESSED

ComboBox: SHORTCUT DETECTED (specKey: CONTROL) MOUSE_PRESSED

ComboBox: SHORTCUT DETECTED (specKey: CONTROL) KEY_PRESSED

0
投票

根据注释中的限制,您可以使用

KeyCombination
,它“代表键盘快捷键中使用的按键组合”。总结完整的示例引用这里

  • 构建所需的快捷方式并根据需要使用其显示文本:
KeyCombination bKey = KeyCombination.keyCombination("Shortcut+[");
Button back = new Button(bKey.getDisplayText());
  • 向场景中添加一个加速器,以在遇到组合时唤起按钮所需的行为:
scene.getAccelerators().put(bKey, this::back);
  • 为了避免重复,请尽可能调用按钮的
    fire()
    方法来调用其现有处理程序:
scene.getAccelerators().put(site.k, (Runnable) () -> {
    site.button.fire();
});
  • 添加
    ActionEvent
    监听器以通常的方式处理事件。
back.setOnAction((ActionEvent e) -> {
    back();
});

© www.soinside.com 2019 - 2024. All rights reserved.