键盘绑定在actionPerformed()上

时间:2014-08-10 19:54:22

标签: java swing key-bindings

如果你在带有掩码的java中添加一个Key Binding - 让我们只用KeyEvent.VK_A来说ActionEvent.ALT_MASK - 然后你执行那个键(ALT + A)但是你释放了alt键 < / em>在'A'键之前,您通常会遇到一个问题,即类(实现ActionListener)中的actionPerformed()将继续被激活。这可能(98%肯定)意味着密钥绑定从未注册密钥已被释放。如果你在alt键之前释放'A'键,你很好,但是 - 就像我说的 - 如果你在另一个键之前释放alt键大约1/10秒,它会不断重复。

注意:这只发生在我的程序(here

如果你不相信我,请亲自尝试一下。以下是我的代码片段:

    public ConwayPanel() {
        super();

        setBackground(new Color(245, 255, 245, 255)); // BG slightly green - all ready

        paused = true; // nothing to play... in FUTURE put cool organism in

        startX = 0; // starting position of the left of the grid
        startY = 0; // starting position of the top of the grid
        zoom = 15; // the width of each cell (EXCLUDING the lines that make up the boundaries)
        cellNum = 1000; // The number of cells

        cells = new boolean[cellNum][cellNum]; // populate cells with false/dead

        currentX = 0; // current x cursor position
        currentY = 0; // current y cursor position

        flipBoundaries = new int[4];

        hideCurrentPos = false; // don't want to hide cursor position unless explicitly told to do so

        defineMaps(); // creates Key enums
        setKeyBindings(); // defines Key and KeyNoMask key bindings
        Timer timer = new Timer(100, new KeyListener());
        timer.start();

        setupMouseListeners(); // creates MouseListener, MouseMotionListener and MouseWheelListener

        setFocusable(true); // make isFocusable() true
        requestFocusInWindow(); // get focus for listeners
    }

    private void defineMaps() {
        for (KeyAltMask key : KeyAltMask.values()) {
            keyMap.put(key, false); // value true when key is pressed - all initiated to false
        }
        for (KeyNoMask key : KeyNoMask.values()) {
            keyNoMaskMap.put(key, false); // value true when key is pressed - all initiated to false
        }
    }

    private void setKeyBindings() {
        InputMap inMap = getInputMap(JComponent.WHEN_FOCUSED/* or... WHEN_IN_FOCUSED_WINDOW*/);
        ActionMap actMap = getActionMap();

        for (final KeyAltMask key : KeyAltMask.values()) {
            KeyStroke pressed = KeyStroke.getKeyStroke(key.getKeyCode(), ActionEvent.ALT_MASK, false); // just right! (not blocking shortcut key and preventing accidental keyboard mishaps)
            KeyStroke released = KeyStroke.getKeyStroke(key.getKeyCode(), ActionEvent.ALT_MASK, true); // just right! (not blocking shortcut key and preventing accidental keyboard mishaps)

            inMap.put(pressed, key.toString() + "pressed");
            inMap.put(released, key.toString() + "released");

            actMap.put(key.toString() + "pressed", new AbstractAction() { // adds each value of Key into a HashMap (when the key is pressed) and puts that HashMap action into ActionMap

                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(ActionEvent e) {
                    keyMap.put(key, true);
                }
            });

            actMap.put(key.toString() + "released", new AbstractAction() { // adds each value of Key into a HashMap (when the key is released) and puts that HashMap action into ActionMap

                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(ActionEvent e) {
                    keyMap.put(key, false);
                }
            });
        }

        for (final KeyNoMask key : KeyNoMask.values()) {
            KeyStroke pressed = KeyStroke.getKeyStroke(key.getKeyCode(), 0, false);
            KeyStroke released = KeyStroke.getKeyStroke(key.getKeyCode(), 0, true);
            inMap.put(pressed, key.toString() + "pressed");
            inMap.put(released, key.toString() + "released");
            actMap.put(key.toString() + "pressed", new AbstractAction() { // adds each value of KeyNoMask into a HashMap (when the key is pressed) and puts that HashMap action into ActionMap

                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(ActionEvent e) {
                    keyNoMaskMap.put(key, true);
                }
            });
            actMap.put(key.toString() + "released", new AbstractAction() { // adds each value of KeyNoMask into a HashMap (when the key is released) and puts that HashMap action into ActionMap

                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(ActionEvent e) {
                    keyNoMaskMap.put(key, false);
                }
            });
        }
    }

    private class KeyListener implements ActionListener { // probably not great to have same name, but "real" KeyListener not imported

        @Override
        public void actionPerformed(ActionEvent e) {
            for (KeyAltMask key : KeyAltMask.values()) { // run through the ALL of the keys
                if (keyMap.get(key)) { // if key in HashMap is true (i.e. the actionPerformed() above set it true)
                    switch(key.toString()) {
                        case "c": // clear all cells and pause if not paused
                            for (int y = 0; y < cellNum; y++) {
                                for (int x = 0; x < cellNum; x++) {
                                    cells[x][y] = false;
                                }
                            }
                            if (!paused) {
                                paused = true;
                            }
                            break;
                        case "f": // fill all cells and pause if not paused
                            for (int y = 0; y < cellNum; y++) {
                                for (int x = 0; x < cellNum; x++) {
                                    cells[x][y] = true;
                                }
                                if (!paused) {
                                    paused = true;
                                }
                            }
                            break;
                        case "i": // invert all cells and pause if not paused
                            for (int y = 0; y < cellNum; y++) {
                                for (int x = 0; x < cellNum; x++) {
                                    cells[x][y] = !cells[x][y];
                                }
                                if (!paused) {
                                    paused = true;
                                }
                            }
                            break;
                        case "l": // lock all cells that have a live/true cell
                            for (int y = 0; y < cellNum; y++) {
                                for (int x = 0; x < cellNum; x++) {
                                    if (cells[x][y]) {
                                        //set Lock
                                    }
                                }
                            }
                            break;
                        case "p": // pause/play
                            paused = !paused;
                            break;
                        case "s": // step once
                            step = true;
                            break;
                        case "h": // hide current cursor position
                            hideCurrentPos = !hideCurrentPos;
                            break;
//                        default:
                    }
                }
            }

            for (KeyNoMask key : KeyNoMask.values()) { //  run through ALL of the keys (this is the beauty of key bindings - you can move the cursor diagonally). I kinda like a pause after the first key press, though
                if (keyNoMaskMap.get(key)) { // if key in HashMap is true (i.e. the actionPerformed() above returned true)
                    switch(key.toString()) { // move cursor position appropriately and pause if not paused
                        case "down":
                            currentY += currentY == cellNum - 1 ? 0 : 1;
                            if (!paused) {
                                paused = true;
                            }
                            break;
                        case "up":
                            currentY -= currentY == 0 ? 0 : 1;
                            if (!paused) {
                                paused = true;
                            }
                            break;
                        case "left":
                            currentX -= currentX == 0 ? 0 : 1;
                            if (!paused) {
                                paused = true;
                            }

                            break;
                        case "right":
                            currentX += currentX == cellNum - 1 ? 0 : 1;
                            if (!paused) {
                                paused = true;
                            }
                            break;
                        case "space": // flip pixel at current cursor position
                            flipCell(currentX, currentY);
                            if (!paused) {
                                paused = true;
                            }
//                        default:
                    }
                }
            }
        }
    }

这是很多代码,但它至少是KeyBindings的标准。所以,我想知道是否有办法解决这个问题。这是os的错还是Java的错,我该如何修复它。我想避免在else中使用actionPerformed(),因为我需要快速。另外,无论如何都要优化actionPerformed()方法,因为它似乎有点棘手。

我只是将它们放在一起,但是它不会在这里做到这一点!小可执行文件:

package bindingstest;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;

/**
 *
 * @author Dylan AND Hovercraft Full Of Eels
 */
public class BindingsTest {
    static Map<Key, Boolean> keyMap = new HashMap<>();

    enum Key { // possibly used in conjunction with mask in order to prevent keyboard mishaps - it will probably be ALT in FUTURE
        a(KeyEvent.VK_A),
        b(KeyEvent.VK_B),
        c(KeyEvent.VK_C),
        d(KeyEvent.VK_D),
        e(KeyEvent.VK_E),
        f(KeyEvent.VK_F);
        private final int keyCode;

        private Key(int keyCode) {
            this.keyCode = keyCode; // KeyEvent.VK_...
        }

        public int getKeyCode() {
            return keyCode;
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        JFrame frame = new JFrame();
        frame.setVisible(true);
        frame.setBounds(50, 50, 1000, 1000);

        JPanel panel = new JPanel();
        panel.setFocusable(true);
        panel.requestFocusInWindow();

        for (Key key : Key.values()) {
            keyMap.put(key, false);
        }

        InputMap inMap = panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        ActionMap actMap = panel.getActionMap();

        for (final Key key : Key.values()) {
            KeyStroke pressed = KeyStroke.getKeyStroke(key.getKeyCode(), ActionEvent.ALT_MASK, false);
            KeyStroke released = KeyStroke.getKeyStroke(key.getKeyCode(), ActionEvent.ALT_MASK, true);

            inMap.put(pressed, key.toString() + "pressed");
            inMap.put(released, key.toString() + "released");

            actMap.put(key.toString() + "pressed", new AbstractAction() {

                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(ActionEvent e) {
                    keyMap.put(key, true);
                }
            });

            actMap.put(key.toString() + "released", new AbstractAction() {

                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(ActionEvent e) {
                    keyMap.put(key, false);
                }
            });
        }

        for (final Key key : Key.values()) {
            KeyStroke pressed = KeyStroke.getKeyStroke(key.getKeyCode(), 0, false);
            KeyStroke released = KeyStroke.getKeyStroke(key.getKeyCode(), 0, true);
            inMap.put(pressed, key.toString() + "pressed");
            inMap.put(released, key.toString() + "released");
            actMap.put(key.toString() + "pressed", new AbstractAction() {

                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(ActionEvent e) {
                    keyMap.put(key, true);
                }
            });
            actMap.put(key.toString() + "released", new AbstractAction() {

                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(ActionEvent e) {
                    keyMap.put(key, false);
                }
            });
        }

        Timer timer = new Timer(100, new KeyListener());
        timer.start();

        frame.add(panel);
    }

    private static class KeyListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            for (Key key : Key.values()) { // run through the ALL of the keys
                if (keyMap.get(key)) { // if key in HashMap is true (i.e. the actionPerformed() above set it true)
                    switch(key.toString()) {
                        case "a":
                            System.out.println("a");
                            break;
                        case "b":
                            System.out.println("b");
                            break;
                        case "c":
                            System.out.println("c");
                            break;
                        case "d":
                            System.out.println("d");
                            break;
                        case "e":
                            System.out.println("e");
                            break;
                        case "f":
                            System.out.println("f");
                    }
                }
            }
        }
    }
}

1 个答案:

答案 0 :(得分:5)

好的,我知道你在说什么,感谢发布可编辑的代码。一种解决方案是使用释放KeyStrokes,一个用于alt键,一个用于普通键。例如,

  InputMap inMap = panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
  ActionMap actMap = panel.getActionMap();

  for (final Key key : Key.values()) {
     KeyStroke altPressed = KeyStroke.getKeyStroke(key.getKeyCode(),
           InputEvent.ALT_DOWN_MASK, false);
     KeyStroke altReleased = KeyStroke.getKeyStroke(key.getKeyCode(),
           InputEvent.ALT_DOWN_MASK, true);
     KeyStroke released  = KeyStroke.getKeyStroke(key.getKeyCode(),
           0, true);

     inMap.put(altPressed, altPressed.toString());
     inMap.put(altReleased, altReleased.toString());
     inMap.put(released, released.toString());

     actMap.put(altPressed.toString(), new AbstractAction() {

        private static final long serialVersionUID = 1L;

        @Override
        public void actionPerformed(ActionEvent e) {
           keyMap.put(key, true);
        }
     });

     Action releaseAction = new AbstractAction() {

        private static final long serialVersionUID = 1L;

        @Override
        public void actionPerformed(ActionEvent e) {
           keyMap.put(key, false);
        }
     };

     actMap.put(altReleased.toString(), releaseAction);
     actMap.put(released.toString(), releaseAction);

另一个解决方案是不执行上述操作,而是在Timer的每次迭代中重新设置Map:

     for (Key key : Key.values()) { // run through the ALL of the keys
        if (keyMap.get(key)) { // if key in HashMap is true (i.e. the
                               // actionPerformed() above set it true)
           switch (key.toString()) {
           case "a":
              System.out.println("a");
              break;
           case "b":
              System.out.println("b");
              break;
           case "c":
              System.out.println("c");
              break;
           case "d":
              System.out.println("d");
              break;
           case "e":
              System.out.println("e");
              break;
           case "f":
              System.out.println("f");
           }

           // ***** add this *****
           keyMap.put(key, Boolean.FALSE);
        }
     }

第二种解决方案会受到操作系统在按下按键时按键提交的延迟的影响。