【问题标题】:How can I get the keyboard scan code in java?如何在java中获取键盘扫描码?
【发布时间】:2013-06-19 18:48:28
【问题描述】:

我想编写一个应用程序,其中键绑定特定于键盘上键的位置,而不是它们映射到的字符。例如,美式键盘上介于 t 和 u 之间的键应执行特定功能,无论它是美国的 Y 还是德国的 Z。

我认为这样做的方法是获取键盘提供给操作系统的实际扫描代码,以表示按下的键。我怎样才能在java中做到这一点?

或者是否有其他方法可以实现相同的功能?

【问题讨论】:

  • 我认为只有通过 JNI/JNA 连接才能实现这一目标

标签: java input keyboard


【解决方案1】:

摘自 Oracle 的 KeyEvent 源代码:

//set from native code.
private transient long rawCode = 0;
private transient long primaryLevelUnicode = 0;
private transient long scancode = 0; // for MS Windows only
private transient long extendedKeyCode = 0;

首先,我考虑解析 KeyEvent 的 toString() 返回值,因为它包含扫描码。但后来我写了一个使用 reflection 的实用方法(在 Windows 8 上成功尝试。):

final public static Integer getScancodeFromKeyEvent(final KeyEvent keyEvent) {

    Integer ret;
    Field field;

    try {
        field = KeyEvent.class.getDeclaredField("scancode");
    } catch (NoSuchFieldException nsfe) {
        System.err.println("ATTENTION! The KeyEvent object does not have a field named \"scancode\"! (Which is kinda weird.)");
        nsfe.printStackTrace();
        return null;
    }

    try {
        field.setAccessible(true);
    } catch (SecurityException se) {
        System.err.println("ATTENTION! Changing the accessibility of the KeyEvent class' field \"scancode\" caused a security exception!");
        se.printStackTrace();
        return null;
    }

    try {
        ret = (int) field.getLong(keyEvent);
    } catch (IllegalAccessException iae) {
        System.err.println("ATTENTION! It is not allowed to read the field \"scancode\" of the KeyEvent instance!");
        iae.printStackTrace();
        return null;
    }

    return ret;
}

显然,之后重新设置字段的可访问性是不必要的,因为我在使用此方法时遇到了异常,而 setAccessible() 行被注释掉了。 (我来回更改它并在运行时仍在运行时在 IntelliJ 中重新编译。在 Eclipse 中应该相同。)不过,首先使用 isAccessible() 方法很容易实现。

我需要扫描码来在键盘上播放音乐,因为在 QWERTZ 和 QWERTY 之间更改键盘语言会自动交换音符。很遗憾,我们无法合法访问类似扫描码的值。上述解决方案成功忽略了当前的键盘布局配置。

顺便说一下,美国键盘布局的“z”和“y”的toString返回值:

[KEY_PRESSED,keyCode=90,keyText=Z,keyChar='z',keyLocation=KEY_LOCATION_STANDARD,rawCode=90,primaryLevelUnicode=122,scancode=44,extendedKeyCode=0x5a] on frame0]
[KEY_PRESSED,keyCode=89,keyText=Y,keyChar='y',keyLocation=KEY_LOCATION_STANDARD,rawCode=89,primaryLevelUnicode=121,scancode=21,extendedKeyCode=0x59] on frame0]

使用 DE 键盘布局:

[KEY_PRESSED,keyCode=89,keyText=Y,keyChar='y',keyLocation=KEY_LOCATION_STANDARD,rawCode=89,primaryLevelUnicode=121,scancode=44,extendedKeyCode=0x59] on frame0]
[KEY_PRESSED,keyCode=90,keyText=Z,keyChar='z',keyLocation=KEY_LOCATION_STANDARD,rawCode=90,primaryLevelUnicode=122,scancode=21,extendedKeyCode=0x5a] on frame0]

注意扫描码。

作为奖励,题外话:

[KEY_PRESSED,keyCode=10,keyText=Enter,keyChar=Enter,keyLocation=KEY_LOCATION_STANDARD,rawCode=13,primaryLevelUnicode=13,scancode=28,extendedKeyCode=0xa] on frame0]
[KEY_PRESSED,keyCode=10,keyText=Enter,keyChar=Enter,keyLocation=KEY_LOCATION_NUMPAD,rawCode=13,primaryLevelUnicode=13,scancode=28,extendedKeyCode=0xa] on frame0]
[KEY_PRESSED,keyCode=49,keyText=1,keyChar='1',keyLocation=KEY_LOCATION_STANDARD,rawCode=49,primaryLevelUnicode=49,scancode=2,extendedKeyCode=0x31] on frame0]
[KEY_PRESSED,keyCode=97,keyText=NumPad-1,keyChar='1',keyLocation=KEY_LOCATION_NUMPAD,rawCode=97,primaryLevelUnicode=49,scancode=79,extendedKeyCode=0x61] on frame0]

【讨论】:

  • 谢谢,这看起来很有用。可悲的是,java 的设计如此糟糕。
  • 有人知道rawCode 的用途吗?
  • 对于 Linux 上的 Oracle 实现,所有键的扫描码始终为 0 :(
  • @HatoruHansou 是的,不幸的是,扫描码字段仅在 Windows 上具有值(至少可以说令人沮丧)。所以他们只是在它后面评论了“// for MS Windows only”。 ://
  • @DreamspacePresident,不,经过我所做的所有测试,我很确定 KeyEvent.scancode 在 Linux 上将始终为 0。但是您可以使用 JNI 以 Linux 方式获取物理密钥代码。我现在不能告诉你 Linux 是否称它们为扫描码,但我确信有一种方法可以使用标准 C 库。例如,SDL 可以报告物理键码,而且……实际上,使用 JNI 加载 SDL 是一个有效的选项,也许是一个矫枉过正的选项。你不需要导入所有的函数,只需要输入处理函数。
【解决方案2】:

正如MadProgrammer所说:你必须使用JNA或JNI。您还可以查看这些项目:

JIntellitype 是用于与 Microsoft Intellitype 交互的 Java API 命令以及在 Java 中注册全局热键 应用。 API 是一个 Java JNI 库,它使用 C++ DLL 来执行 与 Windows 的所有通信。

有类似的项目LinuxMac OS X


JNativeHook 是一个提供全局键盘和鼠标的库 Java 的监听器。这将允许您收听全局快捷方式 或鼠标运动,否则使用纯 Java 是不可能的。到 完成这个任务,JNativeHook 利用平台依赖的原生 代码通过 Java 的本机接口创建低级系统范围 挂钩并将这些事件传递到您的应用程序。


仅限 Windows,支持 Win 7 / 8(32 位和 64 位)


【讨论】:

    猜你喜欢
    • 2014-09-19
    • 2020-12-18
    • 1970-01-01
    • 1970-01-01
    • 2015-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-08
    相关资源
    最近更新 更多