Java的KeyListener的口吃(Java KeyListener stutters)

2019-09-01 16:35发布

我在做Java中的一个非常简单的乒乓球比赛,我做这个用的KeyListener。 我想它,以便当用户按下键盘上的左右键,乓块进入这个方向努力。 这是一个再简单不过的事,但就是我找出的是,当用户按住键,块移动一次,停的时间很短,然后继续移动,直到用户释放键。 我注意到,当你尝试按住电脑上的字母键出现这种情况。 如果我尝试按住“A”键,计算机会做什么:

一个[停顿] aaaaaaaaaaaaaaaa

有什么方法来禁用这个口吃,因为它是在光滑的游戏我的小游戏的方式获得。 速战速决将深表感激。

Answer 1:

我本来约按键绑定一个答案,但一个小的测试后,我发现,他们仍然有同样的口吃问题。

不要依赖于操作系统的重复率。 它可以为每个平台不同,用户还可以自定义。

而是使用一个定时器来安排事件。 你开始一个的keyPressed定时器和停止的keyReleased定时器。

import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.util.Map;
import java.util.HashMap;
import javax.imageio.ImageIO;
import javax.swing.*;

public class KeyboardAnimation implements ActionListener
{
    private final static String PRESSED = "pressed ";
    private final static String RELEASED = "released ";
    private final static Point RELEASED_POINT = new Point(0, 0);

    private JComponent component;
    private Timer timer;
    private Map<String, Point> pressedKeys = new HashMap<String, Point>();

    public KeyboardAnimation(JComponent component, int delay)
    {
        this.component = component;

        timer = new Timer(delay, this);
        timer.setInitialDelay( 0 );
    }

    public void addAction(String keyStroke, int deltaX, int deltaY)
    {
//      InputMap inputMap = component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        InputMap inputMap = component.getInputMap();
        ActionMap actionMap = component.getActionMap();

        String pressedKey = PRESSED + keyStroke;
        KeyStroke pressedKeyStroke = KeyStroke.getKeyStroke( pressedKey );
        Action pressedAction = new AnimationAction(keyStroke, new Point(deltaX, deltaY));
        inputMap.put(pressedKeyStroke, pressedKey);
        actionMap.put(pressedKey, pressedAction);

        String releasedKey = RELEASED + keyStroke;
        KeyStroke releasedKeyStroke = KeyStroke.getKeyStroke( releasedKey );
        Action releasedAction = new AnimationAction(keyStroke, RELEASED_POINT);
        inputMap.put(releasedKeyStroke, releasedKey);
        actionMap.put(releasedKey, releasedAction);
    }

    private void handleKeyEvent(String keyStroke, Point moveDelta)
    {
        //  Keep track of which keys are pressed

        if (RELEASED_POINT == moveDelta)
            pressedKeys.remove( keyStroke );
        else
            pressedKeys.put(keyStroke, moveDelta);

        //  Start the Timer when the first key is pressed

        if (pressedKeys.size() == 1)
        {
            timer.start();
        }

        //  Stop the Timer when all keys have been released

        if (pressedKeys.size() == 0)
        {
            timer.stop();
        }
    }

    //  Invoked when the Timer fires

    public void actionPerformed(ActionEvent e)
    {
        moveComponent();
    }

    //  Move the component to its new location

    private void moveComponent()
    {
        int componentWidth = component.getSize().width;
        int componentHeight = component.getSize().height;

        Dimension parentSize = component.getParent().getSize();
        int parentWidth  = parentSize.width;
        int parentHeight = parentSize.height;

        //  Calculate new move

        int deltaX = 0;
        int deltaY = 0;

        for (Point delta : pressedKeys.values())
        {
            deltaX += delta.x;
            deltaY += delta.y;
        }


        //  Determine next X position

        int nextX = Math.max(component.getLocation().x + deltaX, 0);

        if ( nextX + componentWidth > parentWidth)
        {
            nextX = parentWidth - componentWidth;
        }

        //  Determine next Y position

        int nextY = Math.max(component.getLocation().y + deltaY, 0);

        if ( nextY + componentHeight > parentHeight)
        {
            nextY = parentHeight - componentHeight;
        }

        //  Move the component

        component.setLocation(nextX, nextY);
    }

    private class AnimationAction extends AbstractAction implements ActionListener
    {
        private Point moveDelta;

        public AnimationAction(String keyStroke, Point moveDelta)
        {
            super(PRESSED + keyStroke);
            putValue(ACTION_COMMAND_KEY, keyStroke);

            this.moveDelta = moveDelta;
        }

        public void actionPerformed(ActionEvent e)
        {
            handleKeyEvent((String)getValue(ACTION_COMMAND_KEY), moveDelta);
        }
    }

    public static void main(String[] args)
    {
        JPanel contentPane = new JPanel();
        contentPane.setLayout( null );

        Icon dukeIcon = null;

        try
        {
            dukeIcon = new ImageIcon( "dukewavered.gif" );
//          dukeIcon = new ImageIcon( ImageIO.read( new URL("http://duke.kenai.com/iconSized/duke4.gif") ) );
        }
        catch(Exception e)
        {
            System.out.println(e);
        }

        JLabel duke = new JLabel( dukeIcon );
        duke.setSize( duke.getPreferredSize() );
        duke.setLocation(100, 100);
        contentPane.add( duke );

        KeyboardAnimation navigation = new KeyboardAnimation(duke, 24);
        navigation.addAction("LEFT", -3,  0);
        navigation.addAction("RIGHT", 3,  0);
        navigation.addAction("UP",    0, -3);
        navigation.addAction("DOWN",  0,  3);

        navigation.addAction("A", -5,  0);
        navigation.addAction("S",  5,  0);
        navigation.addAction("Z",  0, -5);
        navigation.addAction("X",  0,  5);
        navigation.addAction("V",  5,  5);

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
//      frame.getContentPane().add(new JTextField(), BorderLayout.SOUTH);
        frame.getContentPane().add(contentPane);
        frame.setSize(600, 600);
        frame.setLocationRelativeTo( null );
        frame.setVisible(true);
    }

}

该代码在Windows,事件的顺序是在keyPressed,的keyPressed,...的keyPressed的keyReleased进行了测试。

不过,我觉得在Mac(或Unix)事件的顺序在keyPressed,调用keyReleased,的keyPressed,调用keyReleased ......,所以我不知道如果此代码将适用于任何比你当前的代码更好。



Answer 2:

  1. 您应该使用键绑定 ,因为他们解决了广大的焦点相关的问题,并且通常更灵活的...
  2. 您需要定义一个标志,当按下一个键表示。 当按下时,你不应该执行任何其他任务...

举些例子...

  • Java对象的运动
  • 使用键盘在java的角度移动的圆
  • Java的Paint方法,荒谬刷新速度的问题 ,其示出了用于同时一个键被按下加速的对象的机构;)

更新了简单的例子

在大多数游戏中,你应该反应“状态变化”而不是实际的关键事件。 这意味着,实际上改变的状态可以是可变的事件(想自定义键)

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class SinglePressKeyBinding {

    public static void main(String[] args) {
        new SinglePressKeyBinding();
    }

    public SinglePressKeyBinding() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JLabel message;

        private boolean spacedOut = false;

        public TestPane() {
            message = new JLabel("Waiting");
            setLayout(new GridBagLayout());
            add(message);

            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "space-pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true), "space-released");

            am.put("space-pressed", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (spacedOut) {
                        message.setText("I'm ignoring you");
                    } else {
                        spacedOut = true;
                        message.setText("Spaced out");
                    }
                }
            });
            am.put("space-released", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    spacedOut = false;
                    message.setText("Back to earth");
                }
            });

        }

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

    }
}


Answer 3:

一个好主意是你想要什么键进行跟踪,然后在keyPressed事件激活布尔的一个设定布尔值,并在释放的键,将其停用。 它会删除键的滞后性,并允许多个按键呢!



文章来源: Java KeyListener stutters