【问题标题】:JPanel doesn't repaint if repaint() is called within JFrame code如果在 JFrame 代码中调用 repaint(),JPanel 不会重新绘制
【发布时间】: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,它也不起作用。

【问题讨论】:

  • 对了,欢迎来到 StackOverflow!
  • 谢谢 :) StackOverflow 非常有用!

标签: java swing jpanel repaint


【解决方案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 分钟,但是哦,好吧
    猜你喜欢
    • 1970-01-01
    • 2011-09-08
    • 1970-01-01
    • 1970-01-01
    • 2012-11-07
    • 1970-01-01
    • 1970-01-01
    • 2019-10-12
    • 1970-01-01
    相关资源
    最近更新 更多