JPanel repaint()方法和调试

时间:2014-11-05 03:23:15

标签: java swing debugging jpanel paint

所以我创建了一个小窗口,在Java上用球弹跳,并试图调试并观察程序的工作。这是该问题的重要代码:

public class Ball {
private static final int SPEED = 2;
private int x = 0, y = 0;
private int xInc = SPEED, yInc = SPEED;

-> public void paint(Graphics2D g) {
    g.fillOval(x, y, 30, 30);
} 
}


public class Game extends JPanel {

Ball gameBall = new Ball();

public void paint(Graphics g) {
    super.paint(g); // cleans the panel
    Graphics2D g2d = (Graphics2D) g;
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
    gameBall.paint(g2d);

}

public void updateBall() {
    gameBall.move(this.getWidth(), this.getHeight());
}

public static void main(String[] args) {
    JFrame mainFrame = new JFrame("Simple Pong");
    mainFrame.setSize(400, 400);
    mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    Game gamePanel = new Game();
    mainFrame.add(gamePanel);
    mainFrame.setVisible(true);

    while (true) {
        try {
            gamePanel.updateBall();
            gamePanel.repaint();
            Thread.sleep(10);
        } catch (InterruptedException e) {
            System.out.println(e);
            System.exit(1);
        }
      }

    }

  }

我为Ball的paint方法的入口设置了一个方法指针,认为每次调用repaint()方法时都会导致程序暂停,因为我认为它调用了paint方法。然后,我可以看到球一次移动一个增量,而不是在重绘方法的位置放置一个线指针。

当我这样做时,程序不会随着我的球的位置的每次更新而停止,而是以似乎随机的增量停止。球将接近边缘,然后在中间,因此必须发生许多油漆调用。

问题:重绘方法总是调用轻量级容器的paint方法吗?如果是这样,为什么调试器不会随着球的每次增量而停止,而是仅停止显示新位置?显然,如果球在跑步时在屏幕上平滑移动,有些东西正在按照我的意愿将Ball对象绘制到我的游戏面板上。

注意:如果我们为Game的paint方法的入口放置一个方法断点,就会发生同样的事情,我已经扩展了JPanel并且是我将球画到的上面。

2 个答案:

答案 0 :(得分:1)

  

重绘方法总是调用轻量级容器的paint方法吗?

没有。对repaint的调用是对更新的请求。重绘管理器可以优化请求并将其合并为单个事件(如果可以的话)。

Swing使用被动绘画算法,该算法旨在提高不需要重新绘制的系统的性能,仅在需要完成时进行绘制。

在旁注中,您应该避免覆盖paint个组件,而是更喜欢使用paintComponent。还要注意,传递给绘制方法的Graphics上下文与其他所有绘制方法共享,对其进行更改可能会产生不良副作用。

相反,您应该创建Graphics上下文状态的快照并对其进行修改...

Graphics2D g2d = (Graphics2D g2d)g.create();
//... Update, modify, transform, do what you want...
g2d.dispose();

请查看Painting in AWT and SwingPerforming Custom Painting了解详情。

Swing在单个线程中运行。也就是说,所有绘画甚至通知都是在事件调度线程的上下文中完成的,有关详细信息,请参阅Concurrency in Swing

当您达到断点时,事件调度线程已停止,但您运行的while-loop不是,因此Ball的x / y位置仍然更新,这意味着当程序恢复时,球将出现在"跳跃"到一个新的位置。

您可以通过在gamePanel.updateBall();上放置断点来测试这一点,UI将继续更新(在每次请求repaint之后),但会逐渐更新,因为球的状态是没有改变。

调试过程不会停止正在运行的其他线程,而只会影响触发断点的线程。

当Java调用main方法时,它就是通过所谓的"主线程"来实现的。当您在setVisible上调用mainFrame时,会创建一个新线程(事件调度线程),这允许UI在while(true)上独立运行,从而阻止它从"挂"用户界面。查看Initial Threads了解更多详情

答案 1 :(得分:1)

repaint没有直接调用paint它只是设置一个变量,以便Swing在某个时候记得调用paint

现在,你知道线程吗?因为这里发生的事情的解释涉及线程。

与Swing有关的一切通常都在一个线程上运行,称为"事件线程"。特别是,在事件线程上调用paint

在名为"主线程"的线程上调用

main。主线程不是事件线程。你的main方法移动球(间接地,通过调用updateBall)。主要线程的代码大致是#34;移动球,设置以后重新粉刷我'变量,重复"。

当你的断点被击中时,只有碰到断点的线程被暂停。这意味着事件线程暂停。更新球的位置的主线程继续运行。当你认为程序暂停时,球的位置会不断更新(因为实际上只有一个线程被暂停)。

当您取消暂停事件线程时,事件线程将返回执行它正在执行的操作(即绘画)。因为,到现在为止,"以后重温我"变量将被设置,它将再次绘制游戏面板 - 但这次球将移动相当远。