使用Java的JComponent repaint()方法

3

我正在用Java编写一个简单的生命游戏程序,但是在实现动画效果时遇到了一些问题。我有一个名为LifeDrawJComponent类,它显示一个像素网格,并具有以下绘制方法:

protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    for (int y = 0; y < lGrid.getHeight(); y++) {
        for (int x = 0; x < lGrid.getWidth(); x++) {
            if (lGrid.getCell(x,y) == 1) {
                g.setColor(Color.red);
                g.fillRect(x * lGrid.getSqSize(), y * lGrid.getSqSize(), lGrid.getSqSize(), lGrid.getSqSize());
            } else {
                g.setColor(Color.white);
                g.fillRect(x * lGrid.getSqSize(), y * lGrid.getSqSize(), lGrid.getSqSize(), lGrid.getSqSize());
            }
        }
    }
}

然后还有另一个类LifeGrid,其中有一个方法run(),调用该方法将更新一个世代的像素网格,然后调用LifeDraw.repaint()。但是,如果我尝试在循环中调用run(),则JComponent不会重新绘制,直到循环完成,因此显示的只是第一个和最后一个世代。我认为可能更新得太快而无法重新绘制,因此尝试在迭代之间使用Thread.sleep(),但仍然存在相同的问题。理论上(或者至少我希望如此),它应该在每次迭代之间重新绘制组件,并显示像素逐渐变化的动画。
我对Java GUI并不是很了解,所以任何帮助都将不胜感激。希望我已经表达清楚了,如果有疑问,请告诉我!

可能是与此处相同的问题:https://dev59.com/okrSa4cB1Zd3GeqPYKQB - user85421
3个回答

1

必须在事件循环中触发Repaint,而不是在另一个线程中。
尝试使用以下代码替换对repaint()的调用:

SwingUtilities.invokeLater(new Runnable()
{
    public void run()
    {
        repaint();
    }
}); 

1
我原以为可以在任何线程中调用repaint()方法,然后它会在适当的线程中调用paintComponent()方法。难道我非常错误吗? - Tyler
你说得对。我非常谨慎,不会在事件循环之外执行GUI操作。 - Paul Tomblin
我认为现在是时候买一本关于Swing的书了,因为有太多我不知道的东西。虽然考虑到Swing的规模,我相当确定那些问题会一直存在 :) - Adam

1

我不确定这是否能解决你的问题,但你可以尝试在计时器中调用run()而不是简单的for或while循环。


非常好,谢谢!完美地运行了。我不太确定为什么之前不能正常工作,但我猜测可能是因为不同的线程在run()处理数组时处于睡眠状态。 非常感谢 :) - Adam
如果问题得到解决,那么很有可能是你的“run()”方法或者调用它的其他方法在事件循环中一直处于忙等待状态。我原本以为这是从Runnable或Thread中的run()方法,但显然不是这样。 - Paul Tomblin

1

repaint()的JavaDocs中:

如果组件正在显示,则将指定区域添加到脏区域列表中。在所有当前挂起的事件被分派后,组件将被重绘。

所有repaint()所做的就是发出需要重新绘制该区域的信号。
尝试使用paintImmediatelyupdate


非常感谢您的帮助;如上所述,使用计时器而不是循环/睡眠(sleep())似乎解决了问题,但为以防万一我会记下其他答案! - Adam
2
一般来说,我发现尝试使用Thread.sleep()解决Swing问题并不是一个好主意。 - Tyler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接