我正在编写一个使用keyListeners的简单绘图程序。它是有效的,但每次它需要画另一个圆时,我都必须使用repaint()方法,否则它不会在使用其中一个箭头键后自动重新绘制屏幕。对于这样一个简单的程序来说,它会消耗太多的CPU(大约50%),这是很好的。关于如何不使用重新绘制()方法,这样它就可以在不消耗所有CPU的情况下做任何需要的事情,有什么想法吗?这是源代码:
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JComboBox;
import javax.swing.JFrame;
public class Game extends JFrame {
int x, y;
public class AL extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
x--;
}
if (keyCode == e.VK_RIGHT) {
x++;
}
if (keyCode == e.VK_UP) {
y--;
}
if (keyCode == e.VK_DOWN) {
y++;
}
}
@Override
public void keyReleased(KeyEvent e) {
}
}
public static void main(String[] args) {
Game game = new Game();
}
public Game() {
addKeyListener(new AL());
setTitle("Game");
setSize(500, 500);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x = 150;
y = 150;
}
@Override
public void paint(Graphics g) {
g.fillOval(x, y, 15, 15);
repaint();
}
}
在绘画方面,你做错了几件事:
- 不要在像
JFrame
这样的顶级零部件上绘制,而是向其添加JPanel
并在其上绘制 - 不要覆盖
paint
,而是覆盖paintComponent
- 不要在绘制的方法(如
paint
和paintComponent
)内部调用repaint
,这将导致递归
此外,请使用密钥绑定而不是密钥侦听器。这里有一个所有东西结合在一起的例子:
class Example extends JPanel {
int x = 0;
int y = 0;
Example() {
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("RIGHT"), "right");
getActionMap().put("right", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
x++;
repaint();
}
});
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
public void paintComponent(Graphics g) {
g.clearRect(0, 0, getWidth(), getHeight());
g.drawRect(x, y, 30, 30);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new Example());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
});
}
}
不要在paint()
内部调用repaint();
。重新喷漆会安排一个paint()
,所以难怪你的CPU会很困难。
就像Kayaman说的那样,你永远不应该从paint()中调用renew()。可以在keyPressed()中调用Frames repaint()方法,以便每次按下键时都会重新绘制Frame。
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
x--;
}
if (keyCode == e.VK_RIGHT) {
x++;
}
if (keyCode == e.VK_UP) {
y--;
}
if (keyCode == e.VK_DOWN) {
y++;
}
Game.this.repaint();
}
/*...*/
@Override
public void paint(Graphics g) {
g.fillOval(x, y, 15, 15);
}
对repaint()
的调用将导致RepaintManager
调用paint()
方法。因此,如果在paint()
内部调用repaint()
,它将无限循环。相反,您可以在执行key-pressed
操作后重新绘制JFrame
。
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_LEFT) {
x--;
}
if (keyCode == KeyEvent.VK_RIGHT) {
x++;
}
if (keyCode == KeyEvent.VK_UP) {
y--;
}
if (keyCode == KeyEvent.VK_DOWN) {
y++;
}
repaint();
}
从paint()
方法中删除repaint()
调用,并如上所述添加它。
但如果有不同的地方你想重新粉刷JFrame
,而你选择了上面的方法,它会再次变得一团糟。因此,您可以使用Timer
来调用repaint()
。
private final javax.swing.Timer timer;
private final int REFRESH_TIME = 100;
public Game() {
addKeyListener(new AL());
setTitle("Game");
setSize(500, 500);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x = 150;
y = 150;
timer = new javax.swing.Timer(REFRESH_TIME, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
如果您愿意,您可以使用另一种方式在一段时间内调用repaint()
一次。主叫CCD_ 24不需要是CCD_。