所以我创建了一个小窗口,在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并且是我将球画到的上面。
答案 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 Swing和Performing 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;移动球,设置以后重新粉刷我'变量,重复"。
当你的断点被击中时,只有碰到断点的线程被暂停。这意味着事件线程暂停。更新球的位置的主线程继续运行。当你认为程序暂停时,球的位置会不断更新(因为实际上只有一个线程被暂停)。
当您取消暂停事件线程时,事件线程将返回执行它正在执行的操作(即绘画)。因为,到现在为止,"以后重温我"变量将被设置,它将再次绘制游戏面板 - 但这次球将移动相当远。