如何知道用户何时真正在Java中释放了密钥?

2022-09-03 02:18:47

(为清楚起见,已编辑)

我想检测用户何时在Java Swing中按下并释放一个键,忽略键盘自动重复功能。我也想要一个纯Java的方法,在Linux,Mac OS和Windows上工作。

要求:

  1. 当用户按下某个键时,我想知道该键是什么键;
  2. 当用户释放一些密钥时,我想知道那是什么密钥;
  3. 我想忽略系统自动重复选项:我希望每次按键只接收一个按键事件,每个按键释放只接收一个按键释放事件;
  4. 如果可能的话,我会使用项目1到3来了解用户是否一次持有多个键(即,她点击“a”并且没有释放它,她点击了“Enter”)。

我在Java中面临的问题是,在Linux下,当用户持有某些键时,会触发许多keyPress和keyRelease事件(因为键盘重复功能)。

我尝试了一些方法,但没有成功

  1. 获取上次发生密钥事件的时间 - 在Linux中,它们对于密钥重复似乎为零,但是,在Mac OS中,它们不是;
  2. 仅当当前键代码与上一个键不同时才考虑事件 - 这样用户就不能连续命中两次相同的键;

下面是代码的基本(非工作)部分:

import java.awt.event.KeyListener;

public class Example implements KeyListener {

public void keyTyped(KeyEvent e) {
}

public void keyPressed(KeyEvent e) {
    System.out.println("KeyPressed: "+e.getKeyCode()+", ts="+e.getWhen());
}

public void keyReleased(KeyEvent e) {
    System.out.println("KeyReleased: "+e.getKeyCode()+", ts="+e.getWhen());
}

}

当用户持有密钥(即“p”)时,系统将显示:

KeyPressed:  80, ts=1253637271673
KeyReleased: 80, ts=1253637271923
KeyPressed:  80, ts=1253637271923
KeyReleased: 80, ts=1253637271956
KeyPressed:  80, ts=1253637271956
KeyReleased: 80, ts=1253637271990
KeyPressed:  80, ts=1253637271990
KeyReleased: 80, ts=1253637272023
KeyPressed:  80, ts=1253637272023
...

至少在 Linux 下,当密钥被保留时,JVM 会不断重新发送所有关键事件。为了使事情变得更加困难,在我的系统(Kubuntu 9.04 Core 2 Duo)上,时间戳不断变化。JVM 以相同的时间戳发送关键的新版本和新按键。这使得很难知道密钥何时真正被释放。

有什么想法吗?

谢谢


答案 1

这可能会有问题。我不能确定(已经很久了),但重复键功能(由底层操作系统而不是Java处理)可能没有为JVM开发人员提供足够的信息来区分这些额外的关键事件和“真实”事件。(顺便说一句,我在1.1.x的OS /2 AWT中对此进行了研究)。

来自 javadoc for KeyEvent:

“按键按下”和“按键释放”事件级别较低,取决于平台和键盘布局。每当按下或释放某个键时,它们都会生成,并且是找出不生成字符输入的键(例如,动作键,修饰键等)的唯一方法。正在按下或释放的键由 getKeyCode 方法指示,该方法返回虚拟键代码。

正如我在OS / 2中这样做时所记得的那样(当时它仍然只有像旧版本的Windows那样的键盘处理的2事件上/下风格,而不是在更现代的版本中获得的3事件向上/向下/char风格),如果键只是被按住并且事件自动生成,我没有报告KeyRelease事件的任何不同;但我怀疑OS /2甚至没有向我报告该信息(无法确定)。我们使用来自Sun的Windows参考JVM作为开发AWT的指南 - 所以我怀疑如果有可能在那里报告这些信息,我至少会看到它们。


答案 2

这个问题在这里重复。

在这个问题中,给出了一个指向太阳错误游行的链接,其中提出了一些解决方法。

我做了一个作为AWTEventListener实现的黑客,可以在应用程序开始时安装。

基本上,观察到释放和随后的PRESSED之间的时间很短 - 实际上,它是0毫。因此,你可以用它作为一个措施:按住释放一段时间,如果一个新的PRESSED紧随其后,然后吞下释放并只处理PRESSED(因此你会得到与Windows上相同的逻辑,这显然是正确的方法)。但是,请注意从一毫秒到下一毫秒的包装(我已经看到这种情况发生) - 因此至少使用1毫秒进行检查。为了解释滞后和其他因素,大约20-30毫秒可能不会造成伤害。