重新喷漆占用太多CPU



我正在编写一个使用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
  • 不要在绘制的方法(如paintpaintComponent)内部调用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_。

最新更新