如果在 JFrame 代码中调用 repaint(),JPanel 不会重新绘制

Posted

技术标签:

【中文标题】如果在 JFrame 代码中调用 repaint(),JPanel 不会重新绘制【英文标题】:JPanel doesn't repaint if repaint() is called within JFrame code 【发布时间】:2016-02-13 16:41:27 【问题描述】:

我有一个类ForestCellularJPanel,它扩展了JPanel 并显示Forest。我编写了一个原始代码来创建JFrameForestCellularJPanel 并将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);
    

如果我在main() 方法中使用上述代码,那么一切正常, CellularJPanel重绘,paintComponent()正常调用。

如果我将相同的代码粘贴到 UI JFrame 按钮单击事件方法,则新的 JFrame 会显示甚至显示 Forest 的初始状态,因为在调用 jFrame.setVisible(true) 时会调用一次 paintComponent。然后 while 循环正在执行,但 CellularJPanel 不会重新绘制,paintComponent 不会被调用。我不知道为什么,也许我应该以某种方式使用SwingUtilities.invokeLater(...)java.awt.EventQueue.invokeLater,但我已经尝试过了,但它不起作用,我做错了什么。

有什么建议吗?

附: 我的目标是在同一个 UI JFrame 中显示CellularJPanel,点击该按钮。但即使我将此面板添加到主 UI JFrame,它也不起作用。

【问题讨论】:

对了,欢迎来到 ***! 谢谢 :) *** 非常有用! 【参考方案1】:

您的问题是在Event Dispatch Thread 上有一个while(true),这将阻止与 UI 相关的任何内容,因为不再处理 UI 事件。

事件调度线程(单个线程)在 UI 事件消息队列中工作,直到它处理您的 while(true) 循环正在运行的那个。然后它会阻止任何进一步的处理,因为它上面有一个无限循环。从该循环调用 SwingUtilities.invokeLater 将无济于事,因为它会将事件发布到事件调度线程,该线程在 while(true) 循环中被阻塞。

所以删除那个循环,而是使用javax.swing.Timer 来为您的事件计时。在计时器事件中,更改 UI 的状态并调用 repaint。定时器事件将与 UI 线程同步,因此允许更改 UI 组件的状态。

【讨论】:

【参考方案2】:

有一个单独的 UI 线程来绘制事物 - 它也是处理按钮点击的线程。在摇摆中,这称为event dispatch thread。如果 UI 线程忙于运行 while 循环,则无法绘制。

您可以通过让您的按钮单击处理程序只运行循环的一次迭代(没有睡眠)来快速验证这一点:forest.update(); forestJpanel.repaint();

您可以从一个单独的线程(如 Timer)自动更新,该线程在循环中调用 repaint/sleep。

【讨论】:

你的回答也可以接受,但TT早2分钟就回答了:) 从技术上讲,时间戳显示我的是第一个 2 分钟,但是哦,好吧

以上是关于如果在 JFrame 代码中调用 repaint(),JPanel 不会重新绘制的主要内容,如果未能解决你的问题,请参考以下文章

Java GUI repaint() 问题?

Java JPanel repaint() 问题?

如何在JFrame中刷新图像?

java中的paintComponent啥时候被调用

如何使用 removeAll() 从 JFrame 中删除所有组件?

Java中的重绘()