简体   繁体   English

java swing键绑定 - 释放键的缺失操作

[英]java swing key bindings - missing action for released key

Having registered key bindings for "SPACE" and "released SPACE" which works as advertised when space is the only key pressed/released, I notice that pressing space, then pressing ctrl (or any other modifier key), then releasing space and finally releasing ctrl will cause the action associated with "SPACE" to be performed, but not the action associated with "released SPACE". 注册了“SPACE”和“已发布的SPACE”的键绑定,当空间是唯一按下/释放的键时,按照广告的方式工作,我注意到按下空格,然后按ctrl(或任何其他修改键),然后释放空间,最后释放ctrl将导致执行与“SPACE”关联的操作,但不会执行与“已释放的SPACE”关联的操作。

What is the preferred way to cause the action to be performed once space is no longer pressed (or a modifier key is pressed simultaneously)? 一旦不再按空格(或同时按下修改键),导致动作执行的首选方法是什么? I only tried this on Windows 7, 64-bit. 我只在Windows 7,64位上试过这个。

import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.AbstractAction;
import javax.swing.KeyStroke;
import java.awt.event.ActionEvent;
import java.awt.Cursor;

class Bind extends JPanel {
  {
    getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "pressed");
    getInputMap().put(KeyStroke.getKeyStroke("released SPACE"), "released");
    getActionMap().put("pressed", new AbstractAction() {
      @Override public void actionPerformed(ActionEvent e) {
        System.out.println("pressed");
        setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); 
      }
    });
    getActionMap().put("released", new AbstractAction() {
      @Override public void actionPerformed(ActionEvent e) {
        System.out.println("released");
        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 
      }
    });
  }
  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override public void run() {
        JFrame f = new JFrame("Key Bindings");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        f.add(new Bind());
        f.setSize(640, 480);
        f.setVisible(true);
      }
    });
  }
}

UPDATE : This is the way to avoid sticky space when accidentally hitting ctrl, alt, or shift before releasing space: 更新 :这是在释放空间之前意外点击ctrl,alt或shift时避免粘滞空间的方法:

import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.AbstractAction;
import javax.swing.KeyStroke;
import java.awt.event.ActionEvent;
import java.awt.Cursor;

class Bind extends JPanel {
  {
    getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "pressed");
    getInputMap().put(KeyStroke.getKeyStroke("released SPACE"), "released");
    getInputMap().put(KeyStroke.getKeyStroke("ctrl released SPACE"), "released");
    getInputMap().put(KeyStroke.getKeyStroke("shift released SPACE"), "released");
    getInputMap().put(KeyStroke.getKeyStroke("shift ctrl released SPACE"), "released");
    getInputMap().put(KeyStroke.getKeyStroke("alt released SPACE"), "released");
    getInputMap().put(KeyStroke.getKeyStroke("alt ctrl released SPACE"), "released");
    getInputMap().put(KeyStroke.getKeyStroke("alt shift released SPACE"), "released");
    getInputMap().put(KeyStroke.getKeyStroke("alt shift ctrl released SPACE"), "released");
    getActionMap().put("pressed", new AbstractAction() {
      @Override public void actionPerformed(ActionEvent e) {
        System.out.println("pressed");
        setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); 
      }
    });
    getActionMap().put("released", new AbstractAction() {
      @Override public void actionPerformed(ActionEvent e) {
        System.out.println("released");
        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 
      }
    });
  }
  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override public void run() {
        JFrame f = new JFrame("Key Bindings");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        f.add(new Bind());
        f.setSize(640, 480);
        f.setVisible(true);
      }
    });
  }
}

Makes sense that the released SPACE event isn't fired when the Control key is still held down. 有意义的是,当Control键仍然按下时,不会触发released SPACE事件。 I would expect a control released SPACE event to be fired. 我希望control released SPACE事件被解雇。

Add the following to your code: 将以下内容添加到您的代码中:

getInputMap().put(KeyStroke.getKeyStroke("control released SPACE"), "released");

For the same reason the SPACE event won't fire if you first hold the Control key down. 出于同样的原因,如果您先按住Control键, SPACE事件将不会触发。 So you would also need to add bindings for control SPACE . 因此,您还需要为control SPACE添加绑定。

You would need to do this for all the modifier keys, which may or may not be a simpler solution than tracking the key events. 您需要对所有修改键执行此操作,这可能是也可能不是跟踪关键事件的简单解决方案。

It's possible that your OS doesn't fire keyReleased events, but only keyPressed and keyTyped events, or some other combination, so check for that first. 您的操作系统可能不会触发keyReleased事件,而只会keyTyped keyPressedkeyTyped事件或其他组合,因此请先检查该事件。 You might just need to check for keyTyped events instead of keyReleased and you'll be done with it. 您可能只需要检查keyTyped事件而不是keyReleased ,您将完成它。

Short answer: 简短回答:

Use a bitmask or an array to keep track of which keys are currently in the "pressed" state, then use those valued to trigger events. 使用位掩码或数组来跟踪当前处于“已按下”状态的键,然后使用那些值来触发事件。 That is, don't use the Swing events directly to trigger responses in your application - you need an extra layer that essentially stores the state of the keyboard, and from that state, takes the relevant actions. 也就是说,不要直接使用Swing事件来触发应用程序中的响应 - 您需要一个基本上存储键盘状态的额外层,并从该状态开始执行相关操作。

There are also methods available ( see the end of this tutorial - "isAltDown", "isCtrlDown" etc. ) to check if modifier keys are pressed when you receive an event like the "Space" key being pressed. 还有一些方法可用( 参见本教程结尾 - “isAltDown”,“isCtrlDown”等 )来检查当您收到按下“Space”键之类的事件时是否按下了修改键。

Long answer: 答案很长:

You're correct that the events get fired when the keys get pressed and released. 你按下并释放按键时会触发事件是正确的。 It kind of has to work that way so that you can support applications that should treat those events separately, as opposed to together. 它必须以这种方式工作,以便您可以支持应该单独处理这些事件的应用程序,而不是一起处理。 One example (though this isn't the only one) is video games on PC where you might be pressing multiple letter/modifier keys at once (for example, A to go left, and W to go forward) and the game has to treat these two event as distinct inputs, as opposed to composite inputs, resulting in your movement going forward-left. 一个例子(虽然这不是唯一一个)是PC上的视频游戏,你可能会同时按下多个字母/修改键(例如, A向左移动, W向前移动)游戏必须对待这两个事件作为不同的输入,而不是复合输入,导致你的前进 - 左移动。

So, what you basically want to do, if you need to deal with composite inputs, is have a simply array of the actions your app needs to respond to, and their associated key bindings (whether single or multi-keys doesn't really matter). 所以,你基本上想做的事情,如果你需要处理复合输入,就有一个简单的应用程序需要响应的动作数组,以及它们相关的键绑定(无论是单键还是多键都不重要) )。 When a key is pressed, you basically turn on a flag for that key that says it's currently "pressed", and clear the flag when it's released. 当按下某个键时,你基本上会打开该键的标志,表示它当前处于“按下”状态,并在释放时清除该标志。

Then, to trigger your events, you just check all keys that are pressed (via checking which key "flags" are active), and if a particular event's key combination is pressed, then the event is fired. 然后,要触发您的事件,您只需检查所有按下的键(通过检查哪些键“标志”处于活动状态),如果按下特定事件的键组合,则触发事件。

If you have fewer than 32 keys that trigger events, then you can actually do this with a bitmask and a 32-bit int value, rather than an array. 如果你有少于32个触发事件的键,那么你实际上可以使用位掩码和32位int值,而不是数组。 In fact, it's much simpler to do it this way if you can. 事实上,如果可以的话,这样做会简单得多。 If you need up to 64 keys, do the same thing with a long . 如果您需要多达64个密钥,请使用long执行相同的操作。 If you have very few keys that trigger events (8 or less, for example) you can use the 8-bit short type. 如果您触发事件的键很少(例如8或更少),则可以使用8位short类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM