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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1457071/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 16:37:01  来源:igfitidea点击:

How to know when a user has really released a key in Java?

javaswingkeypressautorepeatkeyrelease

提问by Luis Soeiro

(Edited for clarity)

(为清晰起见进行了编辑)

I want to detect when a user presses and releases a key in Java Swing, ignoring the keyboard auto repeat feature. I also would like a pure Java approach the works on Linux, Mac OS and Windows.

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

Requirements:

要求:

  1. When the user presses some key I want to know what key is that;
  2. When the user releases some key, I want to know what key is that;
  3. I want to ignore the system auto repeat options: I want to receive just one keypress event for each key press and just one key release event for each key release;
  4. If possible, I would use items 1 to 3 to know if the user is holding more than one key at a time (i.e, she hits 'a' and without releasing it, she hits "Enter").
  1. 当用户按下某个键时,我想知道那是什么键;
  2. 当用户释放某个键时,我想知道那是什么键;
  3. 我想忽略系统自动重复选项:我希望每次按键只接收一个按键事件,每次按键释放只接收一个按键释放事件;
  4. 如果可能,我会使用项目 1 到 3 来了解用户是否一次按住多个键(即,她按了 'a' 并且没有松开它,她按了“Enter”)。

The problem I'm facing in Java is that under Linux, when the user holds some key, there are many keyPress and keyRelease events being fired (because of the keyboard repeat feature).

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

I've tried some approaches with no success:

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

  1. Get the last time a key event occurred - in Linux, they seem to be zero for key repeat, however, in Mac OS they are not;
  2. Consider an event only if the current keyCode is different from the last one - this way the user can't hit twice the same key in a row;
  1. 获取上次发生关键事件的时间 - 在 Linux 中,对于关键重复,它们似乎为零,但是,在 Mac OS 中它们不是;
  2. 仅当当前的 keyCode 与上一个不同时才考虑事件 - 这样用户就不能连续按两次相同的键;

Here is the basic (non working) part of code:

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

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());
}

}

When a user holds a key (i.e, 'p') the system shows:

当用户持有一个键(即“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
...

At least under Linux, the JVM keeps resending all the key events when a key is being hold. To make things more difficult, on my system (Kubuntu 9.04 Core 2 Duo) the timestamps keep changing. The JVM sends a key new release and new key press with the same timestamp. This makes it hard to know when a key is really released.

至少在 Linux 下,当一个键被保持时,JVM 会不断地重新发送所有的键事件。更麻烦的是,在我的系统(Kubuntu 9.04 Core 2 Duo)上,时间戳一直在变化。JVM 发送具有相同时间戳的关键新版本和新按键。这使得很难知道何时真正释放密钥。

Any ideas?

有任何想法吗?

Thanks

谢谢

回答by M1EK

This could be problematic. I can't remember for sure (it's been a long time), but it's likely the repeating-key feature (which is handled by the underlying operating system, not Java) isn't providing enough information for the JVM developer to distinguish those additional key events from the 'real' one. (I worked on this in the OS/2 AWT back in 1.1.x by the way).

这可能有问题。我不记得了(已经很长时间了),但很可能重复键功能(由底层操作系统处理,而不是 Java)没有为 JVM 开发人员提供足够的信息来区分那些额外的来自“真实”事件的关键事件。(顺便说一下,我在 1.1.x 的 OS/2 AWT 中处理过这个问题)。

From the javadoc for KeyEvent:

来自 KeyEvent 的 javadoc:

"Key pressed" and "key released" events are lower-level and depend on the platform and keyboard layout. They are generated whenever a key is pressed or released, and are the only way to find out about keys that don't generate character input (e.g., action keys, modifier keys, etc.). The key being pressed or released is indicated by the getKeyCode method, which returns a virtual key code.

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

As I recall from doing this in OS/2 (which at the time still had only the 2-event up/down flavor of keyboard handling like older versions of Windows, not the 3-event up/down/char flavor you get in more modern versions), I didn't report KeyReleased events any differently if the key was just being held down and the events auto-generated; but I suspect OS/2 didn't even report that information to me (can't remember for sure). We used the Windows reference JVM from Sun as our guide for developing our AWT - so I suspect if it were possible to report this information there, I'd have at least seen it on their end.

我记得在 OS/2 中这样做(当时它仍然只有 2 事件向上/向下键盘处理的风格,就像旧版本的 Windows,而不是你在更多的 3 事件向上/向下/字符风格现代版本),如果只是按住键并且事件自动生成,我不会以任何不同的方式报告 KeyReleased 事件;但我怀疑 OS/2 甚至没有向我报告该信息(具体记不清了)。我们使用来自 Sun 的 Windows 参考 JVM 作为我们开发 AWT 的指南——所以我怀疑是否可以在那里报告这些信息,我至少会在他们的最后看到它。

回答by stolsvik

This question is duplicated here.

这个问题在这里重复。

In that question, a link to the Sun bug paradeis given, where some workaround is suggested.

在那个问题中,给出了一个指向Sun bug parade的链接,其中建议了一些解决方法。

I've made a hackimplemented as an AWTEventListener that can be installed at the start of the application.

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

Basically, observe that the time between the RELEASED and the subsequent PRESSED is small - actually, it is 0 millis. Thus, you can use that as a measure: Hold the RELEASED for some time, and if a new PRESSED comes right after, then swallow the RELEASED and just handle the PRESSED (And thus you will get the same logic as on Windows, which obviously is the correct way). However, watch for the wrap-over from one millisecond to the next (I've seen this happen) - so use at least 1 ms to check. To account for lags and whatnots, some 20-30 milliseconds probably won't hurt.

基本上,观察 RELEASED 和随后的 PRESSED 之间的时间很小 - 实际上,它是 0 毫秒。因此,您可以将其用作一种措施:将 RELEASED 保持一段时间,如果紧随其后出现新的 PRESSED,则吞下 RELEASED 并处理 PRESSED(因此您将获得与在 Windows 上相同的逻辑,这显然是是正确的方法)。但是,请注意从一毫秒到下一毫秒的重叠(我已经看到这种情况发生) - 所以至少使用 1 毫秒来检查。考虑到滞后和诸如此类的问题,大约 20-30 毫秒可能不会受到影响。

回答by Jakub Gemrot

I've refined stolsvik hack to prevent repeating of KEY_PRESSED and KEY_TYPED events as well, with this refinement it works correctly under Win7 (should work everywhere as it truly watches out for KEY_PRESSED/KEY_TYPED/KEY_RELEASED events).

我已经改进了 stolsvik hack 以防止重复 KEY_PRESSED 和 KEY_TYPED 事件,通过这种改进,它可以在 Win7 下正常工作(应该可以在任何地方工作,因为它真正注意 KEY_PRESSED/KEY_TYPED/KEY_RELEASED 事件)。

Cheers! Jakub

干杯! 雅库布

package com.example;

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.swing.Timer;

/**
 * This {@link AWTEventListener} tries to work around for KEY_PRESSED / KEY_TYPED/     KEY_RELEASED repeaters.
 * 
 * If you wish to obtain only one pressed / typed / released, no repeatings (i.e., when the button is hold for a long time).
 * Use new RepeatingKeyEventsFixer().install() as a first line in main() method.
 * 
 * Based on xxx
 * Which was done by Endre St?lsvik and inspired by xxx (hyperlinks stipped out due to stackoverflow policies)
 * 
 * Refined by Jakub Gemrot not only to fix KEY_RELEASED events but also KEY_PRESSED and KEY_TYPED repeatings. Tested under Win7.
 * 
 * If you wish to test the class, just uncomment all System.out.println(...)s.
 * 
 * @author Endre St?lsvik
 * @author Jakub Gemrot
 */
public class RepeatingKeyEventsFixer implements AWTEventListener {

 public static final int RELEASED_LAG_MILLIS = 5;

 private static boolean assertEDT() {
  if (!EventQueue.isDispatchThread()) {
   throw new AssertionError("Not EDT, but [" + Thread.currentThread() + "].");
  }
  return true;
 }

 private Map<Integer, ReleasedAction> _releasedMap = new HashMap<Integer, ReleasedAction>();
 private Set<Integer> _pressed = new HashSet<Integer>();
 private Set<Character> _typed = new HashSet<Character>();

 public void install() {
  Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
 }

 public void remove() {
  Toolkit.getDefaultToolkit().removeAWTEventListener(this);
 }

 @Override
 public void eventDispatched(AWTEvent event) {
  assert event instanceof KeyEvent : "Shall only listen to KeyEvents, so no other events shall come here";
  assert assertEDT(); // REMEMBER THAT THIS IS SINGLE THREADED, so no need
       // for synch.

  // ?: Is this one of our synthetic RELEASED events?
  if (event instanceof Reposted) {
   //System.out.println("REPOSTED: " + ((KeyEvent)event).getKeyChar());
   // -> Yes, so we shalln't process it again.
   return;
  }

  final KeyEvent keyEvent = (KeyEvent) event;

  // ?: Is this already consumed?
  // (Note how events are passed on to all AWTEventListeners even though a
  // previous one consumed it)
  if (keyEvent.isConsumed()) {
   return;
  }

  // ?: KEY_TYPED event? (We're only interested in KEY_PRESSED and
  // KEY_RELEASED).
  if (event.getID() == KeyEvent.KEY_TYPED) {
   if (_typed.contains(keyEvent.getKeyChar())) {
    // we're being retyped -> prevent!
    //System.out.println("TYPED: " + keyEvent.getKeyChar() + " (CONSUMED)");
    keyEvent.consume();  
   } else {
    // -> Yes, TYPED, for a first time
    //System.out.println("TYPED: " + keyEvent.getKeyChar());
    _typed.add(keyEvent.getKeyChar());
   }
   return;
  } 

  // ?: Is this RELEASED? (the problem we're trying to fix!)
  if (keyEvent.getID() == KeyEvent.KEY_RELEASED) {
   // -> Yes, so stick in wait
   /*
    * Really just wait until "immediately", as the point is that the
    * subsequent PRESSED shall already have been posted on the event
    * queue, and shall thus be the direct next event no matter which
    * events are posted afterwards. The code with the ReleasedAction
    * handles if the Timer thread actually fires the action due to
    * lags, by cancelling the action itself upon the PRESSED.
    */
   final Timer timer = new Timer(RELEASED_LAG_MILLIS, null);
   ReleasedAction action = new ReleasedAction(keyEvent, timer);
   timer.addActionListener(action);
   timer.start();

   ReleasedAction oldAction = (ReleasedAction)_releasedMap.put(Integer.valueOf(keyEvent.getKeyCode()), action);
   if (oldAction != null) oldAction.cancel();

   // Consume the original
   keyEvent.consume();
   //System.out.println("RELEASED: " + keyEvent.getKeyChar() + " (CONSUMED)");
   return;
  }

  if (keyEvent.getID() == KeyEvent.KEY_PRESSED) {

   if (_pressed.contains(keyEvent.getKeyCode())) {
    // we're still being pressed
    //System.out.println("PRESSED: " + keyEvent.getKeyChar() + " (CONSUMED)"); 
    keyEvent.consume();
   } else {   
    // Remember that this is single threaded (EDT), so we can't have
    // races.
    ReleasedAction action = (ReleasedAction) _releasedMap.get(keyEvent.getKeyCode());
    // ?: Do we have a corresponding RELEASED waiting?
    if (action != null) {
     // -> Yes, so dump it
     action.cancel();

    }
    _pressed.add(keyEvent.getKeyCode());
    //System.out.println("PRESSED: " + keyEvent.getKeyChar());    
   }

   return;
  }

  throw new AssertionError("All IDs should be covered.");
 }

 /**
  * The ActionListener that posts the RELEASED {@link RepostedKeyEvent} if
  * the {@link Timer} times out (and hence the repeat-action was over).
  */
 protected class ReleasedAction implements ActionListener {

  private final KeyEvent _originalKeyEvent;
  private Timer _timer;

  ReleasedAction(KeyEvent originalReleased, Timer timer) {
   _timer = timer;
   _originalKeyEvent = originalReleased;
  }

  void cancel() {
   assert assertEDT();
   _timer.stop();
   _timer = null;
   _releasedMap.remove(Integer.valueOf(_originalKeyEvent.getKeyCode()));   
  }

  @Override
  public void actionPerformed(@SuppressWarnings("unused") ActionEvent e) {
   assert assertEDT();
   // ?: Are we already cancelled?
   // (Judging by Timer and TimerQueue code, we can theoretically be
   // raced to be posted onto EDT by TimerQueue,
   // due to some lag, unfair scheduling)
   if (_timer == null) {
    // -> Yes, so don't post the new RELEASED event.
    return;
   }
   //System.out.println("REPOST RELEASE: " + _originalKeyEvent.getKeyChar());
   // Stop Timer and clean.
   cancel();
   // Creating new KeyEvent (we've consumed the original).
   KeyEvent newEvent = new RepostedKeyEvent(
     (Component) _originalKeyEvent.getSource(),
     _originalKeyEvent.getID(), _originalKeyEvent.getWhen(),
     _originalKeyEvent.getModifiers(), _originalKeyEvent
       .getKeyCode(), _originalKeyEvent.getKeyChar(),
     _originalKeyEvent.getKeyLocation());
   // Posting to EventQueue.
   _pressed.remove(_originalKeyEvent.getKeyCode());
   _typed.remove(_originalKeyEvent.getKeyChar());
   Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(newEvent);
  }
 }

 /**
  * Marker interface that denotes that the {@link KeyEvent} in question is
  * reposted from some {@link AWTEventListener}, including this. It denotes
  * that the event shall not be "hack processed" by this class again. (The
  * problem is that it is not possible to state
  * "inject this event from this point in the pipeline" - one have to inject
  * it to the event queue directly, thus it will come through this
  * {@link AWTEventListener} too.
  */
 public interface Reposted {
  // marker
 }

 /**
  * Dead simple extension of {@link KeyEvent} that implements
  * {@link Reposted}.
  */
 public static class RepostedKeyEvent extends KeyEvent implements Reposted {
  public RepostedKeyEvent(@SuppressWarnings("hiding") Component source,
    @SuppressWarnings("hiding") int id, long when, int modifiers,
    int keyCode, char keyChar, int keyLocation) {
   super(source, id, when, modifiers, keyCode, keyChar, keyLocation);
  }
 }

}

回答by Elias Vasylenko

I have found a solution to this problem without relying on timing (which according to some users is not necessarily consistent 100% of the time) but instead by issuing extra key presses to override the key repeat.

我找到了解决此问题的方法,而不依赖于时间(根据某些用户的说法,时间不一定是 100% 一致),而是通过发出额外的按键来覆盖键重复。

To see what I mean, try holding a key and then hitting another mid-stream. The repeat will stop. It seems that, on my system at least, the key hits issued by Robot also have this effect.

要明白我的意思,请尝试按住一个键然后点击另一个中游。重复将停止。看来,至少在我的系统上,Robot 发出的击键也有这个效果。

For an example implementation, tested in Windows 7 & Ubuntu, see:

有关在 Windows 7 和 Ubuntu 中测试的示例实现,请参阅:

http://elionline.co.uk/blog/2012/07/12/ignore-key-repeats-in-java-swing-independently-of-platform/

http://elionline.co.uk/blog/2012/07/12/ignore-key-repeat-in-java-swing-independently-of-platform/

Also, thanks to Endre Stolsvik's solution for showing me how to do a global event listener! Appreciated.

另外,感谢 Endre Stolsvik 的解决方案向我展示了如何做一个全局事件监听器!赞赏。

回答by ziggystar

I've found a solution that does without waiting in case you have something like a game loop going. The idea is storing the release events. Then you can check against them both inside the game loop and inside the key pressed handler. By "(un)register a key" I mean the extracted true press/release events that should be processed by the application. Take care of synchronization when doing the following!

我找到了一个无需等待的解决方案,以防您遇到游戏循环之类的问题。这个想法是存储发布事件。然后您可以在游戏循环内和按键处理程序内检查它们。“(取消)注册密钥”是指提取的真实按下/释放事件,应由应用程序处理。执行以下操作时请注意同步!

  • on release events: store the event per key; otherwise do nothing!
  • on press events: if there is no stored release event, this is a new press -> register it; if there is a stored event within 5 ms, this is an auto-repeat -> remove its release event; otherwise we have a stored release event that hasn't been cleared by the game loop, yet -> (fast user) do as you like, e.g. unregister-register
  • in your loop: check the stored release events and treat those that are older than 5 ms as true releases; unregister them; handle all registered keys
  • 释放事件:存储每个键的事件;否则什么都不做!
  • 关于新闻事件:如果没有存储的发布事件,这是一个新的新闻 -> 注册它;如果在 5 毫秒内有存储的事件,这是一个自动重复 -> 删除它的释放事件;否则我们有一个尚未被游戏循环清除的存储释放事件,但是 ->(快速用户)按你的意愿做,例如取消注册注册
  • 在您的循环中:检查存储的发布事件并将那些早于 5 毫秒的事件视为真正的发布;取消注册;处理所有注册的密钥

回答by Devon_C_Miller

Save the timestamp of the event (arg0.when()) in keyReleased. If the next keyPressedevent is for the same key and has the same timestamp, it is an autorepeat.

将事件 ( arg0.when())的时间戳保存在keyReleased. 如果下一个keyPressed事件针对相同的键并且具有相同的时间戳,则它是自动重复。

If you hold down multiple keys, X11 only autorepeats the last key pressed. So, if you hold down 'a' and 'd' you'll see something like:

如果您按住多个键,X11 只会自动重复最后一次按下的键。所以,如果你按住 'a' 和 'd' 你会看到类似的内容:

a down
a up
a down
d down
d up
d down
d up
a up

回答by Savvas Dalkitsis

You might want to use the action map of the component you are interested in. Here's an example that deals with a specific key (SPACE BAR) but I'm sure that if you read the documentation you may be able to modify it to handle generic key presses and releases.

您可能想要使用您感兴趣的组件的动作映射。这是一个处理特定键(空格键)的示例,但我确信如果您阅读文档,您可能能够修改它以处理通用按键按下和释放。

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;

import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;

public class Main {
    public static void main(String[] args) {
        JFrame f = new JFrame("Test");
        JPanel c = new JPanel();

        c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
                KeyStroke.getKeyStroke("SPACE"), "pressed");
        c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
                KeyStroke.getKeyStroke("released SPACE"), "released");
        c.getActionMap().put("pressed", new Action() {
            public void addPropertyChangeListener(
                    PropertyChangeListener listener) {
            }

            public Object getValue(String key) {
                return null;
            }

            public boolean isEnabled() {
                return true;
            }

            public void putValue(String key, Object value) {
            }

            public void removePropertyChangeListener(
                    PropertyChangeListener listener) {
            }

            public void setEnabled(boolean b) {
            }

            public void actionPerformed(ActionEvent e) {
                System.out.println("Pressed space at "+System.nanoTime());
            }
        });
        c.getActionMap().put("released", new Action() {
            public void addPropertyChangeListener(
                    PropertyChangeListener listener) {
            }

            public Object getValue(String key) {
                return null;
            }

            public boolean isEnabled() {
                return true;
            }

            public void putValue(String key, Object value) {
            }

            public void removePropertyChangeListener(
                    PropertyChangeListener listener) {
            }

            public void setEnabled(boolean b) {
            }

            public void actionPerformed(ActionEvent e) {
                System.out.println("Released space at "+System.nanoTime());
            }
        });
        c.setPreferredSize(new Dimension(200,200));


        f.getContentPane().add(c);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.pack();
        f.setVisible(true);
    }
}

回答by luvieere

Well, you said that it is possible that the time between key events in case of key repeat be non-negative. Even so, it is likely very short. You could then threshold this time to some very small value, and everything equal or lower than it be considered a key repeat.

好吧,您说在重复键的情况下,键事件之间的时间可能是非负的。即便如此,它也可能很短。然后您可以将这次阈值设置为某个非常小的值,并且所有等于或低于它的都被视为关键重复。

回答by c0d3rman

This approach stores key presses in a HashMap, resetting them when the key is released. Most of the code is courtesy of Elistin thispost.

这种方法将按键存储在一个 HashMap 中,当按键被释放时重置它们。大部分的代码是礼貌ELIST这个岗位。

import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

public class KeyboardInput2 {
    private static HashMap<Integer, Boolean> pressed = new HashMap<Integer, Boolean>();
    public static boolean isPressed(int key) {
        synchronized (KeyboardInput2.class) {
            return pressed.get(key);
        }
    }

    public static void allPressed() {
        final Set<Integer> templist = pressed.keySet();
        if (templist.size() > 0) {
            System.out.println("Key(s) logged: ");
        }
        for (int key : templist) {
            System.out.println(KeyEvent.getKeyText(key));
        }
    }

    public static void main(String[] args) {
        KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() {

            @Override
            public boolean dispatchKeyEvent(KeyEvent ke) {
                synchronized (KeyboardInput2.class) {
                    switch (ke.getID()) {
                        case KeyEvent.KEY_PRESSED:
                            pressed.put(ke.getKeyCode(), true);
                            break;
                        case KeyEvent.KEY_RELEASED:
                            pressed.remove(ke.getKeyCode());
                            break;
                        }
                        return false;
                }
            }
        });
    }
}

You can use the HashMap to check if a certain key is pressed, or call KeyboardInput2.allPressed()to print every pressed key.

您可以使用 HashMap 来检查某个键是否被按下,或者调用KeyboardInput2.allPressed()打印每个按下的键。

回答by Dreamspace President

What am I not getting about all the elaborate but questionable suggestions? The solution is sosimple!(Overlooked the key part of OP's question: "under Linux, when the user holds some key, there are many keyPress and keyRelease events being fired")

我对所有精心制作但有问题的建议有什么误解?解决办法就是这么简单!(忽略了OP问题的关键部分:“在Linux下,当用户持有某个键时,会触发许多keyPress和keyRelease事件”)

In your keyPress event, check if the keyCode is already in a Set<Integer>. If it is, it must be an autorepeat event. If it is not, put it in and digest it. In your keyRelease event, blindly remove the keyCode from the Set - assuming that OP's statement about many keyRelease eventsis false. On Windows, I only get several keyPresses, but only one keyRelease.

在您的 keyPress 事件中,检查 keyCode 是否已经在 Set<Integer> 中。如果是,则它必须是自动重复事件。如果不是,请将其放入并消化。在您的 keyRelease 事件中,盲目地从 Set 中删除 keyCode - 假设 OP 关于许多 keyRelease 事件的声明是错误的。在 Windows 上,我只得到几个 keyPresses,但只有一个 keyRelease。

To abstract this a little, you could create a wrapper that can carry KeyEvents, MouseEvents, and MouseWheelEvents and has a flag that already says that the keyPress is just an autorepeat.

为了稍微抽象一下,您可以创建一个包装器,它可以携带 KeyEvents、MouseEvents 和 MouseWheelEvents,并且有一个标志已经表明 keyPress 只是一个自动重复。