问题
How do I code an event that starts when a key (specifically the space bar) is pressed, continues running when the key is held, and only stops when the key is released? I'm trying to simulate a wheeled object that moves across a rough surface. I've tried using the original KeyListener methods but the problem is, when I hold the space bar the object I'm simulating repeatedly stops and starts. I've heard a possible solution is key bindings but I still don't understand them even after reading the Java tutorial about it.
Here's the paint method used for the simulation (controlled by a Thread which sleeps every 10 milliseconds):
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Shape roadsurface = new Rectangle2D.Float(0, 85, 1000, 200);
g2.setColor(Color.BLACK);
g2.setStroke(new BasicStroke(10));
g2.draw(roadsurface);
g2.setColor(new Color(102, 102, 153));
g2.fill(roadsurface);
Image carimage = Toolkit.getDefaultToolkit().getImage("cargrey.png");
g2.drawImage(carimage, x_pos, y_pos, 60, 30, this);
g2.finalize();
}
And here's the methods used to change x_pos (variables not declared are assumed to have been declared in the class body):
public void accelerate()
{
do
{ acc = 15.0 - t;
vel = ( t * 15.0) - ( 0.5 * Math.pow(t, 2.0) );
disp = ( 0.5 * 15.0 * Math.pow(t, 2.0) ) - ( (1.0/6.0) * Math.pow(t, 3.0) );
x_pos = (int)disp;
t += 0.01; break;} while (acc > 0);
while (acc <= 0)
{ acc = 0;
disp = t * vel;
x_pos = (int)disp;
t += 0.01;
}
}
public void brake(double vel, double disp)
{
double u = 0;
double disp2;
while (vel > 0)
{
disp2 = (vel * u) + (0.5 * -100 * Math.pow(u, 2.0) );
vel = vel + (-100 * u);
x_pos = (int)(disp + disp2);
u += 0.01;
t += 0.01; break;}
while (vel <= 0)
{
u += 0.01;
t += 0.01;
}
}
This was my initial idea for the event:
class Key1 extends Thread implements KeyListener
{
Track g;
boolean keyIsPressed;
Key1(Track g)
{
this.g = g;
}
public void keyTyped(KeyEvent ke) {}
public void keyPressed(KeyEvent ke)
{
if (ke.getKeyCode() == KeyEvent.VK_SPACE)
keyIsPressed = true;
}
public void keyReleased(KeyEvent ke)
{
if (ke.getKeyCode() == KeyEvent.VK_SPACE)
keyIsPressed = false;
}
public void run()
{
while (keyIsPressed)
{
g.repaint();
g.accelerate();
try
{
Thread.sleep(10);
}
catch (InterruptedException ex)
{
// swallowed
}
while (!keyIsPressed)
{
g.repaint();
g.brake(g.vel, g.disp);
try
{
Thread.sleep(10);
}
catch (InterruptedException ex)
{
// swallowed
}
}
}
回答1:
One of the best and most common approaches is to have a flag for each mapped key. When it is pressed (detected by a KeyEvent), the flag is set to true. When it is released (also detected by a KeyEvent), the flag is set to false.
The application states (periodically checked by another thread) would be determined not by the Key state or event, but by the flag state.
This simple approach avoids the implications that are caused by key repetition setups.
回答2:
I would be the first to argue that sometimes a KeyListener
is good thing, but I don't think this is one of them.
Basically, this demonstrates how it might be possible to use Key Bindings to monitor the state change of a key (in this the space bar).
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
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.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class KeyBindingTest {
public static void main(String[] args) {
new KeyBindingTest();
}
public KeyBindingTest() {
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 boolean spaceIsDown = false;
public TestPane() {
// Avoid all the issues with focusable and single
// focused components
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) {
spaceIsDown = true;
repaint();
}
});
am.put("space.released", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
spaceIsDown = false;
repaint();
}
});
}
public boolean isSpaceIsDown() {
return spaceIsDown;
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
String text = isSpaceIsDown() ? "Space is DOWN" : "Space is UP";
FontMetrics fm = g2d.getFontMetrics();
g2d.drawString(text, (getWidth() - fm.stringWidth(text)) / 2, (((getHeight() - fm.getHeight())) / 2) + fm.getAscent());
g2d.dispose();
}
}
}
来源:https://stackoverflow.com/questions/15145120/key-listeners-key-bindings-in-java