pierwotnie miał odpowiedź na temat klawiszy, ale po trochę badań stwierdziliśmy, że nadal miał ten sam zacinać problem.
Nie należy polegać na częstości powtarzania systemu operacyjnego. Może być różna dla każdej platformy, a użytkownik może ją również dostosować.
Zamiast tego użyj Timera, aby zaplanować wydarzenie. Uruchamiasz Timer na keyPressed i zatrzymujesz Timer na 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);
}
}
Kod ten został przetestowany na Windows, gdzie kolejność zdarzeń jest keyPressed, keyPressed, keyPressed ... keyReleased.
Jednak myślę, że na Macu (lub Unixie) kolejność zdarzeń to keyPressed, keyReleased, keyPressed, keyReleased ... Więc nie jestem pewien, czy ten kod będzie działał lepiej niż twój obecny kod.
Pierwotnie miałem odpowiedź na temat powiązań kluczowych, ale po niewielkich testach odkryłem, że nadal mają ten sam problem z jąkaniem. +1 dla innych rzeczy. – syb0rg
@ syb0rg Dlatego potrzebujesz tej małej flagi;). Ostatni przykład demonstruje zgrabny pomysł, aby umożliwić zdarzeniu kluczowemu zastosowanie delty przyspieszenia, im dłużej zostanie naciśnięty, tym bardziej zwiększa się delta, dopóki nie zostanie zwolniony, a delta zostanie odwrócona, spowalniając obiekt w czasie;) – MadProgrammer
@ syb0rg Dodano prosty przykład demonstrujący zasadę;) – MadProgrammer