重绘占用太多CPU

时间:2016-01-02 14:40:16

标签: java swing cpu-usage repaint

我正在编写一个使用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();
}

}

4 个答案:

答案 0 :(得分:3)

在绘画方面,你做了一些错事:

  • 不要在JFrame这样的顶级组件上绘画,而是在其上添加JPanel并在其上绘画。
  • 请勿覆盖paint,而是覆盖paintComponent
  • 不要在绘制的方法中调用repaint(如paintpaintComponent),它会导致递归。

此外,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);
            }
        });
    }
}

答案 1 :(得分:2)

请勿在{{1​​}}内拨打repaint();。重绘计划paint(),所以难怪你的CPU有困难。

答案 2 :(得分:1)

就像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);
}

答案 3 :(得分:0)

致电repaint()会导致RepaintManager调用paint()方法。因此,如果您在repaint()内拨打paint(),它将无限循环。相反,您可以在执行JFrame操作后重新绘制key-pressed

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

repaint()方法移除paint()来电并按上述方式添加。

但是如果你想要重新绘制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