Подтвердить что ты не робот

Java Swing KeyBindings перестает работать только на Mac

Я работаю над игрой с помощью Swing, и я использую KeyBindings для ввода с клавиатуры.

У меня возникают проблемы, когда KeyBindings перестают отвечать. Это происходит каждый раз, когда я запускаю приложение, но, насколько я могу судить, это не когда происходит определенная цепочка событий. KeyBindings просто прекращают прием ввода с клавиатуры. Я также использую ввод мыши, который продолжает работать, поэтому я знаю, что он не связан с входом вообще.

Некоторые вещи, которые я пробовал:

  • удостоверился, что мой объект не был собран мусором
  • искал определенную причину возникновения проблемы (например: через определенное время, нажатие определенной комбинации клавиш), к которой я не мог найти никого
  • попытался использовать KeyListener вместо

Ни один из них не работал.

Затем я скопировал проект (без изменения какого-либо кода) на машину Windows, и после тестирования я не мог решить эту проблему один раз. Я не вставлял код здесь, потому что я на 99% положителен, это не связано с моим кодом, но связано с операционной системой, на которой он работает.

Это проблема с macOS или JDK для Mac, или что-то еще, о чем я не знаю?

Я использую обновление JDK версии 8 112, на компьютере работает macOS Sierra версии 10.12.2.

Кажется, у кого-то была та же проблема, которую я сейчас делаю, но у них никогда не было ответа.

ИЗМЕНИТЬ

Вот пример кода, где возникает проблема:

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

//an example of the problem where the keyboard stops receiving input randomly
public class ProblemExample extends JPanel {
    private static final long serialVersionUID = 1L;

    private int xPos = 200, yPos = 200;
    private boolean wKey, aKey, sKey, dKey;
    private BufferedImage image; //sprite

    public ProblemExample() {
        JFrame frame = new JFrame();
        frame.add(this);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        //makes the sprite a red square
        image = new BufferedImage(50, 50, BufferedImage.TYPE_INT_ARGB);
        int[] redPixels = new int[50 * 50];
        for (int i = 0; i < redPixels.length; i++) {
            redPixels[i] = 0xffff0000;
        }
        image.setRGB(0, 0, 50, 50, redPixels, 0, 50);
        initializeKeys();
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(800, 600);
    }

    //sets up Key Bindings
    private void initializeKeys() {
        final String W = "W",
                     A = "A", 
                     S = "S", 
                     D = "D",
                     PRESSED = "PRESSED",
                     RELEASED = "RELEASED";

        InputMap inputMap = this.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
        ActionMap actionMap = this.getActionMap();

        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), W + PRESSED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), A + PRESSED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), S + PRESSED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), D + PRESSED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), W + RELEASED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), A + RELEASED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), S + RELEASED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), D + RELEASED);

        Action wActionPressed = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                wKey = true;
            }
        };
        Action aActionPressed = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                aKey = true;
            }
        };
        Action sActionPressed = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                sKey = true;
            }
        };
        Action dActionPressed = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                dKey = true;
            }
        };
        Action wActionReleased = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                wKey = false;
            }
        };
        Action aActionReleased = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                aKey = false;
            }
        };
        Action sActionReleased = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                sKey = false;
            }
        };
        Action dActionReleased = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                dKey = false;
            }
        };

        actionMap.put(W + PRESSED, wActionPressed);
        actionMap.put(A + PRESSED, aActionPressed);
        actionMap.put(S + PRESSED, sActionPressed);
        actionMap.put(D + PRESSED, dActionPressed);
        actionMap.put(W + RELEASED, wActionReleased);
        actionMap.put(A + RELEASED, aActionReleased);
        actionMap.put(S + RELEASED, sActionReleased);
        actionMap.put(D + RELEASED, dActionReleased);
    }

    public void loop() {
        if (wKey) yPos -= 5;
        if (aKey) xPos -= 5;
        if (sKey) yPos += 5;
        if (dKey) xPos += 5;
        repaint();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, xPos, yPos, null);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                ProblemExample example = new ProblemExample();
                Timer timer = new Timer(60, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        example.loop();
                    }
                });
                timer.start();
            }
        });
    }

}
4b9b3361

Ответ 1

Вы можете подумать, что это ошибка в MAC, в то время как это не так. У MAC есть функция вторичных ключей.
Поскольку вы являетесь пользователем MAC, Я надеюсь, что вы знаете об этой функции. Эта функция может быть причиной вашей проблемы.
Вспомогательная ключевая функция MAC: используется, когда удерживание клавиши букв будет отображать варианты этой буквы, например, удерживая "u", чтобы получить "ü". Это пригодится при написании неанглийских слов.

Theres простой способ взять под контроль и изменить поведение длинного нажатия клавиш, чтобы удовлетворить ваши потребности.

Откройте приложение "Терминал" и напишите:

defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false

Затем перезапустите любое открытое приложение, в которое вы хотите активировать этот параметр.

REVERTING BACK: Просто добавьте true вместо false к предыдущей команде, например:

defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool true

ОБНОВЛЕНИЕ:
Вы можете ускорить скорость повторений клавиш или уменьшить задержку до того, как удерживаемая клавиша начнет повторяться, перейдя к системным настройкам и внеся изменения под заголовком клавиатуры.

Ответ 2

Помимо того, что Tahir Hussain Mir предлагает изменить параметры Mac-ключа, я считаю, что проблема кроется в цикличности, было бы более эффективно реализовать ее в управляемом событиями образом, я взял на себя смелость изменить код немного. Вместе с Hussain Mir предложите решение, оно должно решить вашу проблему.

Вы также можете обрабатывать клавиши, повторяя себя, например, начиная таймер при нажатии клавиши и останавливая его при отпускании клавиши, но тогда поведение нажатия клавиш будет отличаться между Windows и Mac, что на самом деле не так, как вы хотите идти.

package swing;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;

//an example of the problem where the keyboard stops receiving input randomly
public class SOMacKeyBindings extends JPanel
{
    private BufferedImage image; //sprite
    private Point point = new Point(200, 200);
    private int steps = 5;

    private class KeyAction extends AbstractAction
    {
        private Runnable runnable;

        public KeyAction(Runnable runnable)
        {
            this.runnable = runnable;
        }

        @Override
        public void actionPerformed(ActionEvent e)
        {
            runnable.run();
        }
    }

    public SOMacKeyBindings()
    {
        JFrame frame = new JFrame();
        frame.add(this);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        //makes the sprite a red square
        image = new BufferedImage(50, 50, BufferedImage.TYPE_INT_ARGB);
        int[] redPixels = new int[50 * 50];
        for (int i = 0; i < redPixels.length; i++)
        {
            redPixels[i] = 0xffff0000;
        }
        image.setRGB(0, 0, 50, 50, redPixels, 0, 50);
        initializeKeys();
    }

    @Override
    public Dimension getPreferredSize()
    {
        return new Dimension(800, 600);
    }

    //sets up Key Bindings
    private void initializeKeys()
    {
        final String W = "W",
                A = "A",
                S = "S",
                D = "D",
                PRESSED = "PRESSED";

        InputMap inputMap = this.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
        ActionMap actionMap = this.getActionMap();

        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), W + PRESSED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), A + PRESSED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), S + PRESSED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), D + PRESSED);

        actionMap.put(W + PRESSED, new KeyAction(() -> { point.y -= steps; repaint(); }));
        actionMap.put(A + PRESSED, new KeyAction(() -> { point.x -= steps; repaint(); }));
        actionMap.put(S + PRESSED, new KeyAction(() -> { point.y += steps; repaint(); }));
        actionMap.put(D + PRESSED, new KeyAction(() -> { point.x += steps; repaint(); }));
    }

    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.drawImage(image, point.x, point.y, null);
    }

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(() -> new SOMacKeyBindings());
    }
}