Java JPanel repaint()方法及其调试

Java JPanel repaint()方法及其调试,java,swing,debugging,jpanel,paint,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); } }

所以我用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方法。然后,我可以看着球一次移动一个增量,而不是在重绘方法所在的位置放置一个线指针

当我这样做时,程序不会随着球的位置的每次更新而停止,而是以看起来像随机增量的方式停止。球将接近边缘,然后在中间,这么多的油漆呼叫必须发生。 问题:对于我的轻量级容器,repaint方法是否总是调用paint方法?如果是这样的话,为什么调试器不随着球的每次增量而停止,而只是有时停止显示新的位置?很明显,如果球在运行时在屏幕上平稳移动,则会有东西完全按照我的希望将球对象绘制到我的游戏面板上

注意:如果我们为游戏的绘制方法的入口放置一个方法断点,也会发生同样的情况,我已经扩展了JPanel,这就是我绘制球的地方

repaint方法是否总是为我的轻量级容器调用paint方法

否。调用
repaint
是请求更新。重绘管理器可以优化请求,并在可能的情况下将其整合到单个事件中

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

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

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

Graphics2D g2d = (Graphics2D g2d)g.create();
//... Update, modify, transform, do what you want...
g2d.dispose();
请查看和以了解更多详细信息

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

当您到达断点时,事件调度线程停止,但您正在运行的
while循环
没有停止,因此
的x/y位置仍然被更新,这意味着当程序恢复时,球将显示为“跳转”到新位置

您可以通过在
gamePanel.updateBall()上放置断点来测试这一点,UI将继续更新(在每次请求重新绘制后),但会随着而增量更新,因为球的状态没有改变

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

当Java调用
main
方法时,它是从所谓的“主线程”调用的。在
大型机上调用
setVisible
时,将创建一个新线程(事件调度线程),这允许UI在
上独立运行,而(true)
,防止它“挂起”UI。查看更多详细信息

重新绘制
不直接调用
绘制
。它只是设置了一个变量,以便Swing在某个时候记住调用
paint

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

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

在名为“主线程”的线程上调用
main
。主线程不是事件线程。您的
main
方法移动球(通过调用
updateBall
间接地移动球)。主线程的代码大致是“移动球,设置'repain me later'变量,重复”

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


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

那么,如果没有调用我的重写方法,程序如何知道将球放置在何处呢?这是一种错觉。帧之间有足够的变化,允许球“出现”移动…这是动画的本质…任何人都希望突出显示投票失败的原因,以便我可以从您的观察中学习,甚至可能修复我做错的事情?但当运行时,球会像预期的那样在屏幕上平稳移动。调试时,它会在不同的位置出现,而不是以小的增量出现。为什么画法…这是唯一一个有关于我的球放在哪里的说明的东西。。。只会随机出现,但似乎每帧都会被调用。@mad程序员我对你不回答这个问题投了反对票,除非我误解了这个问题。非常感谢!我越来越生气了。你真的不能再清楚地回答我的问题了。@immibis还有一件事,如果main方法仍在运行,并且只有事件线程暂停,那么在main方法中的事件威胁上运行的方法(如repaint)会发生什么情况?@bmcentee148
repaint
wi