令人费解的小写字母 KeyEvent 处理

Puzzling KeyEvent handling for lowercase letters

如果我如下设置 KeyEvent 处理程序,对于大写字母和非字母或数字的 Ascii 字符,行为如我所料,并且正如 Oracle 文档似乎指定的那样。

但是,对于小写字母,如果我在输入字母时按住 Ctrl 键,EventHandler 似乎根本无法注册按键。

下面的输出显示了这一点,依次键入 Q、q、$、Ctrl+Q、Ctrl+q、Ctrl+$。

任何人都可以阐明这种行为吗?我错过了什么吗?我已经搜索但没有找到任何其他参考这个问题。

import javafx.application.*;
import javafx.stage.*;
import javafx.scene.*;
import javafx.event.*;
import javafx.scene.input.KeyEvent;

// Test handling keypresses of upper and lower case letter, and non-alphameric ascii symbol,
// to see if any difference exists when Ctrl is held down
// between the handling for (ev.isShortcutDown()) and (ev.isMetaDown() || ev.isControlDown())
// and between the three character types

// Program to be tested by typing, in sequence,
//              Q  q  $  Ctrl+Q  Ctrl+q  Ctrl+$


public class TestHandlingKeyEvents extends Application {

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

    public void start(Stage stage) throws Exception {
        Scene scene = new Scene(new Group());
        stage.setScene(scene);

        EventHandler<KeyEvent> handleUserTyping = ev -> {

            String charTyped = ev.getCharacter();
            switch (charTyped) {
                case "Q":               // quit
                    System.out.println("\nQ typed ");
                    if (ev.isShortcutDown()) 
                        System.out.println("with Shortcut down ");
                    if (ev.isMetaDown() || ev.isControlDown())
                        System.out.println("with Meta or Ctrl down ");
                    break;                  
                case "q":               // complete reset
                    System.out.println("\nq typed ");
                    if (ev.isShortcutDown()) 
                        System.out.println("with Shortcut down ");
                    if (ev.isMetaDown() || ev.isControlDown())
                        System.out.println("with Meta or Ctrl down ");              
                    break;
                case "$":               // back to last view
                    System.out.println("\n$ typed ");
                    if (ev.isShortcutDown()) 
                        System.out.println("with Shortcut down ");
                    if (ev.isMetaDown() || ev.isControlDown())
                        System.out.println("with Meta or Ctrl down ");              
                    break;
            }
        };

        scene.setOnKeyTyped(handleUserTyping);
        stage.show();
    }
}

并且输出:

john@jlaptop2:/java$ java TestHandlingKeyEvents

Q typed 

q typed 

$ typed 

Q typed 
with Shortcut down 
with Meta or Ctrl down 

$ typed 
with Shortcut down 
with Meta or Ctrl down 
john@jlaptop2:/java$ 

您使用了错误的事件类型来获得您想要的行为。

阅读 KeyEvent javadoc 中的各种类型的 KeyEvent。

基本上,您可以在 KEY_PRESSED 事件而不是 KEY_TYPED 事件上注册事件处理程序。仅当底层系统注册了通过键入生成的 unicode 字符时,才会生成键键入事件,而对于您尝试捕获的某些键输入序列,情况并非如此。要捕获更多不一定与 unicode 字符生成相关的输入字符序列,可以使用较低级别的 KEY_PRESSED(或 KEY_RELEASED)事件。

不过,您可以使用比这更高级别的接口并且可能更适合您的用例,即根据 KeyCodeCombinations.

设置加速器

使用加速器的示例

import javafx.application.Application;
import javafx.collections.ObservableMap;
import javafx.scene.*;
import javafx.scene.input.KeyCombination;
import javafx.stage.Stage;

// Test handling keypresses of upper and lower case letter, 
// and non-alphameric ascii symbol.    
// Program to be tested by typing, in sequence,
//              Ctrl+Q  Ctrl+q  Ctrl+$    
public class TestHandlingKeyAccelerators extends Application {    
    public void start(Stage stage) throws Exception {
        Scene scene = new Scene(new Group());
        stage.setScene(scene);

        ObservableMap<KeyCombination, Runnable> accelerators = 
                scene.getAccelerators();

        accelerators.put(
                KeyCombination.valueOf("Ctrl+Q"), 
                () -> System.out.println("Ctrl+q -> reset")
        );
        accelerators.put(
                KeyCombination.valueOf("Ctrl+Shift+Q"), 
                () -> System.out.println("Ctrl+Q -> quit")
        );
        accelerators.put(
                KeyCombination.valueOf("Ctrl+Shift+'4'"), 
                () -> System.out.println("Ctrl+$ -> back to last view")
        );

        stage.show();
    }

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

注意 KeyCombination.valueOf 方法是如何用于构造组合键的。还要注意(标识字符的“4”除外)每个键如何由其键码标识。如,在我的键盘上,小写q和大写Q是同一个键,只是用了一个shift来生成大写键,那么Ctrl+q编码为Ctrl+Q,Ctrl+Q编码为Ctrl+Shift+问。另外,我的键盘没有美元键,而是通过按 shift 键和四键生成美元。通常四键代码是 DIGIT4,但出于某种原因,这在这种情况下不起作用。因此,为了获得 Ctrl+$ 操作,我使用 Ctrl+Shift+'4' 来表示按下生成字符“4”的键时的控制和移位修饰符。

评论一下你原样的操作

此外,作为主要是琐事的旁注,在 Mac (2012 MacBook Air OS X 10.11.4, Java 8u60 上进行测试时),您的程序给出不同的输出,可能是由于不同的键盘系统或 OS 对按键事件的处理。建议的键序列在 Mac 上的输出是:

Q typed 

q typed 

$ typed 

您在问题中显示的以下输出序列不是在 Mac 上生成的:

Q typed 
with Shortcut down 
with Meta or Ctrl down 

$ typed 
with Shortcut down 
with Meta or Ctrl down