我有一个类Forest
和CellularJPanel
,它扩展了JPanel
并显示了Forest
。我写了一个原始代码来创建JFrame
、Forest
、CellularJPanel
并将CellularJPanel
添加到JFrame
。接下来是一个无限循环,它使Forest
更新和CellularJPanel
重新绘制。
JFrame jFrame = new JFrame();
Forest forest = new Forest();
CellularJPanel forestJPanel = new CellularJPanel(forest);
jFrame.add(forestJPanel);
jFrame.pack();
//jFrame.setResizable(false);
jFrame.setLocationRelativeTo(null);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setVisible(true);
while (true)
{
try
{
forestJPanel.repaint();
forest.update();
forest.sleep(); // calls Thread.sleep(...)
}
catch (InterruptedException e)
{
}
}
下面是 CellularJPanel
类的代码:
public class CellularJPanel extends JPanel
{
private CellularAutomata cellularAutomata;
public CellularJPanel(CellularAutomata cellularAutomata)
{
super();
this.cellularAutomata = cellularAutomata;
setPreferredSize(this.cellularAutomata.getDimension());
}
@Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D graphics2D = (Graphics2D)g;
cellularAutomata.draw(graphics2D);
}
}
如果我在< code>main()方法中使用上面的代码,那么一切正常,< code>CellularJPanel重新绘制,< code>paintComponent()被正常调用。
如果我将相同的代码粘贴到UI JFrame按钮click事件方法,那么new JFrame会显示甚至显示< code>Forest的初始状态,因为在调用< code > JFrame . set visible(true)时,会调用< code>paintComponent。然后执行< code>while循环,但< code>CellularJPanel不重新绘制,不调用< code>paintComponent。我不知道为什么,也许我应该使用< code > swing utilities . invokelater(...)或者< code > Java . awt . event queue . invoke later ,但是我试过了,没有用,我做错了。
有什么建议吗?
P.S. 我的目标是在同一个UI JFrame中显示CellularJPanel
,从中单击按钮。但是即使我将此面板添加到主UI JFrame,它也无法正常工作。
您的问题是在事件调度线程上有一个while(true)
,它将阻止与UI相关的任何内容,因为UI事件不再得到处理。
事件调度线程(单个线程)沿着 UI 事件消息队列向下工作,直到它处理运行 while(true)
循环的线程。然后,它会阻止任何进一步的处理,因为它上有一个无限循环。从该循环调用 SwingUtilities.invokeLater
将无济于事,因为它将事件发布到事件调度线程,该线程在 while(true)
循环中被阻止。
因此,请删除该循环,而是使用javax. swing.Timer
来为您的事件计时。在计时器事件中,更改UI的状态并调用重新绘制
。计时器事件将与UI线程同步,因此允许更改UI组件的状态。
有一个绘制东西的UI线程,它也是处理按钮点击的线程。在swing中,这被称为事件调度线程。如果UI线程忙于运行< code>while循环,它就不能进行绘制。
你可以通过让你的按钮点击处理程序只运行循环的一次迭代(没有睡眠)来快速验证这一点:forest.update();forestJpanel.repair()代码>
您可以从一个单独的线程(如Timer
)自动更新,该线程在循环中调用rep户/睡眠。