【问题标题】:JPanel repaint() method and debuggingJPanel repaint() 方法和调试
【发布时间】:2014-11-05 03:23:15
【问题描述】:

所以我用 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 方法?如果是这样,为什么调试器不会随着球的每次增量而停止,而是有时只停止以显示新位置?显然,如果球在运行时在屏幕上平稳移动,则某些东西正在将 Ball 对象完全按照我的意愿绘制到我的游戏面板上。

注意:如果我们为 Game 的绘制方法的入口放置一个方法断点,也会发生同样的事情,我扩展了 JPanel 并且我在上面绘制了球。

【问题讨论】:

    标签: java swing debugging jpanel paint


    【解决方案1】:

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

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

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

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

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

    当您取消暂停事件线程时,事件线程将返回做它正在做的事情(即绘画)。由于此时“稍后重绘我”变量已设置,它将再次绘制游戏面板 - 但这次球移动得相当远。

    【讨论】:

    • 非常感谢!我变得非常生气。你真的不能再清楚地回答我的问题了。
    • @immibis 还有一件事,如果主方法实际上仍在运行并且只有事件线程被暂停,那么在位于主方法中的事件威胁上运行的方法会发生什么,例如作为重绘?
    • @bmcentee148 repaint 将调用RepaintManager 请求进行更新,RepaintManager 可能会忽略其中一些,但很可能重绘请求已添加到EventQueue,等待 EDT 解锁并处理它们。 RepaintManager 可能有可能“删除”待处理的请求,但我不记得上次我在代码的那部分挖掘时看到过这种机制......这可能会导致你的绘画方法被锤击一旦 EDT 被解除阻塞,快速连续...
    • @bmcentee148 没有所谓的跨线程方法调用。 main 在主线程上被调用,它调用repaint,所以那些特定的repaint 调用在主线程上运行。通常这是一个禁忌(因为它通常有效,但有时无效,并且可能导致难以跟踪的错误),但 repaint 是您可以调用的函数之一线程。
    • 好的,所以当事件线程被暂停时,那些在主线程中重新绘制的调用实际上并没有显示在面板上。这实际上是如何工作的?似乎它们只是被忽略了,因为我点击播放按钮的速度越快,球就越接近它的最后一个位置。由此我认为主线程肯定会更新它的位置,只是不显示它,即使在主线程上调用了 repaint 方法。
    【解决方案2】:

    repaint 方法是否总是为我的轻量级容器调用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 在单个线程中运行。也就是说,所有的绘制甚至通知都是在 Event Dispatching Thread 的上下文中完成的,详情请参阅Concurrency in Swing

    当你到达你的断点时,Event Dispatching Thread is STOPPED,但你正在运行的while-loop没有,所以Ball的x/y位置仍然被更新,这意味着当程序恢复时,球会出现“跳”到一个新位置。

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

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

    当Java 调用main 方法时,它是从所谓的“主线程”调用的。当您在 mainFrame 上调用 setVisible 时,会创建一个新线程(事件调度线程),这允许 UI 在 while(true) 上独立运行,防止它“挂起”UI。查看Initial Threads了解更多详情

    【讨论】:

    • 那么如果我的重写方法没有被调用,那么程序如何知道将球放在哪里?
    • 这是一种错觉。帧之间的变化足以让球“出现”移动……这就是动画的本质……
    • 任何人都希望突出投票失败的原因,以便我可以从您的观察中学习,甚至可以解决我做错的事情?
    • 但是当跑动时,球会像预期的那样平稳地穿过屏幕。调试时,它从一个地方出现到另一个地方,而不是小幅增加。为什么paint方法......这是唯一有关于放置我的球的说明......只是随机发生但似乎每帧都被调用。
    • @MadProgrammer 我对你没有回答这个问题投了反对票,除非我误解了这个问题。
    猜你喜欢
    • 2023-03-06
    • 1970-01-01
    • 1970-01-01
    • 2011-09-08
    • 1970-01-01
    • 2014-08-14
    • 1970-01-01
    • 2013-12-25
    • 1970-01-01
    相关资源
    最近更新 更多