【问题标题】:Painting a JPanel hides menu绘制 JPanel 隐藏菜单
【发布时间】:2013-03-08 19:17:15
【问题描述】:

我有一个 JPanel,可以在其上执行自定义绘画,这可能需要 15 秒。

问题是,当我在绘制 JPanel 时单击菜单时,绘制是在菜单上完成的,并且选项不可见。 (看图片中扭曲的文字)

如果我在绘画完成后单击菜单,则不会出现意外行为,但是当我将鼠标指针移到不同的菜单上时,同样的事情又会发生。 例如,绘画完成后,如果我点击“应用程序”,不会出现副作用,但是当我将鼠标指针移到“帮助”上时,就会出现前面描述的行为。 如何避免这种情况?

我正在一个单独的线程中的 paintComponent() 方法之外执行绘画:

public class RandomIFSPanel extends JPanel {

  RandomIFSPanelRenderer randomIFSPanelRenderer;

 @Override
    public void paintComponent(Graphics graphics) {
        try {
            randomIFSPanelRenderer.stop();
        } catch (Exception e) {
        }
randomIFSPanelRenderer = new RandomIFSPanelRenderer();
   randomIFSPanelRenderer.start();
}

    class RandomIFSPanelRenderer extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < numberOfPoints; i++) {
                Graphics g=getGraphics();
             g.setColor(Color.WHITE);
               g.drawLine........
            }
        }
    }

}

【问题讨论】:

  • 你的线程很可能是这里的问题 - 如果你没有在 EDT 上完成所有绘画,那么你就是在自找麻烦/这样的怪癖。如果你使用SwingUtilities.invokeLater(),问题还存在吗?
  • Graphics g=getGraphics(); 是问题的另一部分。您应该考虑将整个绘画表面替换为BufferedImage(显示在JLabel)并将长期运行的工作转移到SwingWorker。
  • @berry120 如果我使用 SwingUtilities.invokeLater(randomIFSPanelRenderer) 而不是 randomIFSPanelRenderer.start(),则窗口在调整大小时变得无响应。
  • @AndrewThompson 整个绘画过程需要时间,我希望它能够实时完成,即我希望用户看到更新后的图像,因为像素被逐一计算和绘制。
  • “我想要实时完成” 我想要一匹会飞的小马,但这并不意味着我会得到一个。 Java GUI 体系​​结构无法支持“实时”方面。遵循我在第一条评论中概述的策略,使用BufferedImage,每 100 次左右更新,在标签上调用repaint()。如果 JRE 不忙,将呈现这些更改,但如果它忙于进行其他 GUI 更新,则该调用可能会与其他重绘调用合并。您所做的一切都不会实现“实时”更新。

标签: java swing


【解决方案1】:

除了已经说过的关于事件调度线程的内容:

  • 你是直接调用paintComponent()方法吗?调用 repaint() 更“安全”。
  • 是否可以在调用paintComponent() 时缓冲图像、绘制图像然后在面板上进行设置?

【讨论】:

  • 当用户单击“绘制”按钮时,我正在调用 randomIFSPanel.repaint()。 RandomIFSPanel 是显示绘图的窗口的黑白部分。
  • 整幅画需要很多时间,具体取决于用户输入的值。我想用每个像素更新图像,因为它是实时计算的。因此,无法缓冲图像。
  • 可能会缓冲图像并每隔 100 毫秒左右显示一次结果。如果问题没有消失,请尝试在您的框架中调用 repaint()。
【解决方案2】:

首先,您不应该在paintComponent() 方法中启动线程,因为paintComponent() 可能会被repaint() 多次调用。可能会发生新线程启动而之前的线程没有终止的情况。

其次,如果要正常绘制菜单,请尝试在paintComponent()的开头添加super.paintComponent(g)。

第三,Graphics g = getGraphics(); 为什么不直接使用graphics?由于 Graphics 对象正在发生变化,因此您不能假设下一幅画是在同一个旧 Graphics 对象中进行的。

【讨论】:

  • 虽然我没有在我的问题中说明,但在开始新的之前,我首先要停止以前的 randomIFSPanelRenderer 线程。我已经修改了问题中的代码以反映它。我不明白你说“为什么不只使用图形”是什么意思。能详细说明一下吗?
  • 在paintComponent() 开头添加super.paintComponent(g) 不起作用。
  • @Ranjith-SR2GF 您在paintComponent() 方法中定义了graphics。这是您应该使用的,而不是调用getGraphics()。同样,即使你停止了前一个线程,在paintComponent() 方法中创建和启动一个新线程仍然不好。再次调用时如何确保上一个线程完成。
  • @Ranjith-SR2GF 与其在方法中创建线程,不如在外部创建线程并在线程中调用repaint()。
【解决方案3】:
  • 不要使用线程会阻​​塞 EDT,不是所有的事件都可以在屏幕上看到,在某些情况下只有第一个。最后,改用 Swing Timer

  • 不要在paintComponent中创建任何Object、FileIO、Thread等,也不要在try-catch-finally块中创建不安全

  • 之前创建任何对象,将其放入数组和paintComponent 内部以在最终对象数组中循环

  • 发布SSCCE,简短,可运行,可编译,只是关于绘画,所有对象都应存储为局部变量(实际上,任何关于二次渐变和缩小问题的答案)

【讨论】:

    【解决方案4】:

    我不会在paintComponent 方法中运行线程。我会在paint方法之外使用线程。

    【讨论】:

    • 线程需要 JPanel 的 Graphics 对象,所以我需要使用 getGraphics.drawXYZ() 之类的语句,如果我在绘制方法之外有该线程,则无法完成。
    猜你喜欢
    • 2023-03-12
    • 1970-01-01
    • 2012-06-09
    • 1970-01-01
    • 1970-01-01
    • 2012-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多