重绘占用太多CPU
Repaint taking up too much CPU
我正在编写一个使用 keyListeners 的简单绘图程序。它可以工作,但每次需要绘制另一个圆圈时,我都必须使用 repaint() 方法,否则在使用其中一个箭头键后它不会自动重新绘制屏幕。对于这样一个简单的程序,它会用掉太多 CPU(大约 50%)。关于如何不使用 repaint() 方法以便它可以做任何它需要做的事情而不用尽我所有的 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();
}
}
不要在 paint()
中调用 repaint();
。 Repaint 安排 paint()
,所以难怪你的 CPU 很难过。
就像 Kayaman 所说的那样,您永远不应该从 paint() 中调用 repaint()。
您可以在 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()
。调用 Thread
不必是 EDT
.
在绘画方面你做错了一些事情:
- 不要在像
JFrame
这样的顶级组件上绘制,而是向其添加 JPanel
并在其上绘制。
- 不要覆盖
paint
,而是覆盖 paintComponent
。
- 不要在绘制方法(如
paint
和paintComponent
)中调用repaint
,这会导致递归。
此外,use key bindings instead of key listeners。这是所有内容组合在一起的示例:
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);
}
});
}
}
我正在编写一个使用 keyListeners 的简单绘图程序。它可以工作,但每次需要绘制另一个圆圈时,我都必须使用 repaint() 方法,否则在使用其中一个箭头键后它不会自动重新绘制屏幕。对于这样一个简单的程序,它会用掉太多 CPU(大约 50%)。关于如何不使用 repaint() 方法以便它可以做任何它需要做的事情而不用尽我所有的 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();
}
}
不要在 paint()
中调用 repaint();
。 Repaint 安排 paint()
,所以难怪你的 CPU 很难过。
就像 Kayaman 所说的那样,您永远不应该从 paint() 中调用 repaint()。 您可以在 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()
。调用 Thread
不必是 EDT
.
在绘画方面你做错了一些事情:
- 不要在像
JFrame
这样的顶级组件上绘制,而是向其添加JPanel
并在其上绘制。 - 不要覆盖
paint
,而是覆盖paintComponent
。 - 不要在绘制方法(如
paint
和paintComponent
)中调用repaint
,这会导致递归。
此外,use key bindings instead of key listeners。这是所有内容组合在一起的示例:
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);
}
});
}
}