Arhn - архитектура программирования

KeyBindings застряли на actionPerformed()

Если вы добавите привязку клавиш в java с маской — скажем, ActionEvent.ALT_MASK с KeyEvent.VK_A — и затем вы нажмете эту клавишу (ALT + A), НО, вы отпустите клавишу alt просто перед клавишей «A» вы обычно сталкиваетесь с проблемой, когда actionPerformed() в классе (реализующем ActionListener) будет продолжать активироваться. Это, вероятно (уверен на 98%), означает, что привязка ключей никогда не регистрировала освобождение ключа. Если вы отпустите клавишу «A» перед клавишей alt, все в порядке, но, как я уже сказал, если вы отпустите клавишу alt, возможно, за 1/10 секунды до другой клавиши, она будет повторяться.

Примечание: видимо, это происходит только в моей программе (здесь)

Попробуйте сами, если не верите мне. Вот фрагмент моего кода:

    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. Итак, мне было интересно, есть ли способ обойти это. Является ли это ошибкой ОС или ошибкой 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");
                    }
                }
            }
        }
    }
}
10.08.2014

  • "Try it for yourself if you don't believe me." -- Я бы с удовольствием, но для этого вы должны сначала создать и опубликовать свою небольшую исполняемую/компилируемую программу с минимальным кодом потому что ваш фрагмент не скомпилируется и не запустится для меня. "It's a lot of code,..." -- и в этом прелесть минимальной программы-примера, -- она ​​не содержит большого количества кода, что значительно облегчает нам переход к сути проблемы. 10.08.2014
  • Обратите внимание, что часть этого кода подозрительно похожа на мой код. 10.08.2014
  • Не то чтобы в этом что-то не так. ;) 11.08.2014
  • @HovercraftFullOfEels Хм, я узнал об этом пару месяцев назад и не могу вспомнить, где я это взял, но именно так я это реализую. Кажется, я нашел это на кодовом ранчо некоторое время назад. Если это ваш код, то я не знаю, что вам сказать. Я добавлю вас в авторство. Хотя вы выложили его в сеть без авторских прав. 11.08.2014
  • Кроме того, теперь есть компилируемый код 11.08.2014
  • ............Спасибо 1+ 11.08.2014

Ответы:


1

Хорошо, я понимаю, о чем вы говорите, и спасибо за публикацию компилируемого кода. Одно решение для использования обоих выпусков KeyStrokes, одного для alt-key и одного для обычного ключа. Например,

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

Другое решение состоит в том, чтобы не делать вышеописанное, а переустанавливать Карту с каждой итерацией Таймера:

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

Это второе решение страдает от задержки, которую ОС вносит в отправку нажатия клавиши, когда клавиша удерживается нажатой.

10.08.2014
  • Спасибо. Эта половина устраняет другую проблему (когда я нажимаю клавишу один раз, она часто повторяется как минимум два раза), однако я боюсь, что это не решает проблему и создает другую проблему: повторение все еще происходит, и - кроме того - теперь я не могу перемещаться в две стороны клавишами со стрелками и использовать другую клавишу (без маски). Вот ВЕСЬ мой код (извините, я не добавил вас сюда как автора, потому что уже вставил его в pastebin): http://pastebin.com/LxXXshXu. Если вы хотите, вы можете просто вставить это в Netbeans/Eclipse/текстовый редактор, запустить его и попытаться повторить привязку клавиш. 11.08.2014
  • У меня есть два класса, один с основным (называется Conways.java), а другой расширяет JPanel (извините, кажется, я сказал JFrame), который называется ConwayPanel.java. 11.08.2014
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..