Java中的键监听器/键绑定

时间:2013-02-28 20:33:23

标签: java swing paint keylistener key-bindings

如何对按键(特别是空格键)时启动的事件进行编码,当键保持时继续运行,并且仅在释放键时停止?我试图模拟一个在粗糙表面上移动的轮式物体。我尝试过使用原始的KeyListener方法,但问题是,当我按住空格键时,我正在反复模拟的对象会停止并启动。我听说过一个可能的解决方案是关键绑定,但即使在阅读了Java教程之后我仍然不理解它们。

这是用于模拟的绘制方法(由每10毫秒休眠一次的Thread控制):

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

以下是用于更改x_pos的方法(未声明的变量假定已在类主体中声明):

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

这是我对这次活动的最初想法:

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

}

2 个答案:

答案 0 :(得分:4)

最好和最常见的方法之一是为每个映射键设置一个标志。按下它(由KeyEvent检测)时,该标志设置为true。当它被释放时(也被KeyEvent检测到),该标志被设置为false。

应用程序状态(由另一个线程定期检查)不是由Key状态或事件确定的,而是由标志状态确定的。

这种简单的方法避免了由密钥重复设置引起的影响。

答案 1 :(得分:2)

我会第一个认为有时KeyListener是好事,但我不认为这是其中之一。

基本上,这演示了如何使用键绑定来监视键的状态变化(在空格键中)。

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

}