【问题标题】:Why is paintComponent() continuously and asynchronously being called without explicit repaint() call?为什么在没有显式 repaint() 调用的情况下连续和异步调用paintComponent()?
【发布时间】:2013-07-09 10:57:26
【问题描述】:

所以这个问题有两个部分,我认为可能是相关的,而且大部分是抽象的。简而言之,这就是我正在做的事情:

我有一个JFrame 和一个JPanel 和一些child JPanels,每个都有3 个JButtons。我还为JFrame(即myJFrame.setGlassPane(glassPanel))创建了一个名为glassPanelJComponent,它允许我在JPanels 和按钮上进行绘制。

(1) 本质上是通过单击 JPanel 上的所有 3 个按钮触发的,glassPanel 设置为 Visible(然后似乎调用 paintComponent())。这与第一个问题有关。

(2) 在paintComponent() 中,我使用double bufferglassPanel 上绘制和绘制矩形和图像。这与第二个问题有关。

这是我的相关 GlassPanel 类代码(这不是 SSCCE,因为它现在是一个抽象问题):

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import javax.swing.JComponent;


public class GlassPanel extends JComponent {

     @Override
     protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        setDoubleBuffered(true);
        Graphics2D g2 = (Graphics2D) g;

        g2.drawRect(x,y,width,height);
        g2.fillRect(x,y,width,height);

        g2.drawImage(img, x, y, this);
    }
}

通过在paintComponent() 方法中放置System.out.print 语句,我可以告诉它正在被连续调用并且也是异步调用。关于我认为调用的方式,请参见(1)。另外,假设我绝对确定代码中的任何地方都没有调用repaint()(我已经检查了很多次)。这是第一个问题的基础。

我第一次单击 3 个按钮时,一切顺利。矩形和图像都立即绘制。但是,当我单击接下来的 3 个按钮时(此时,glassPanel 已经是 setVisible(true) 并且第一个矩形和图像仍在屏幕上,绘制在前 3 个按钮上),第二个矩形和图像 只加载部分。当我从 JFrame 之外单击并进入运行程序的 Eclipse 窗口时,对 paintComponent() 的调用次数每次都会以相同的数量快速增加,并且 部分加载的图像和矩形立即完全显示在背景中JFrame。当我单击返回 JFrame 时,调用次数又增加了一个确切的数量)。这是第二个问题的基础。

更新:Here's 我读到的东西:

另外,当 GUI 被另一个窗口覆盖然后变成 未发现,绘画系统调用paintComponent方法 绘画区域等于新发现的区域。

我的问题是:

(1) 为什么在没有repaint() 的情况下,paintComponent() 会被如此称呼?或者,一个类似的问题,什么可能调用paintComponent()?

更新:在做了一些数学运算之后,我坚信每个组件(所有按钮和面板)都会连续调用它。但是,仍然没有调用 repaint()...

(2) 为什么在我从 JFrame 窗口获得焦点之前,图像会部分加载?

请注意,我已经尝试了很多事情:(a) 创建我自己的 doubleBuffer 并且不使用双缓冲区(我知道它主要用于动画),(b) 覆盖而不是覆盖 paintComponent(),(c) 绘图而不是绘图图像(矩形仍然需要时间来加载),(d)绝对确保没有repaint(),(e)使用和不使用SwingUtilities.invokeLater(new Runnable() { public void run() { //stuff});,(f)做了一个if语句只设置可见(真) ) 一次。

如果必须,我可以尝试通过 SSCCE,但我确实认为这些更抽象。谢谢!

【问题讨论】:

  • 对不起,安德鲁。我知道帮助处理您面前的代码(运行)会容易得多。我知道我不会让事情变得容易。但我希望有人遇到过类似的问题,并且可以在没有看到任何代码的情况下回答。
  • 使用 System.out.print 将鼠标、KeyListener 添加到 Glasspane/JComponent 并从 API 中实现的方法调用paintComponent
  • 我会对听众做什么?

标签: java swing paintcomponent repeat glasspane


【解决方案1】:

嗯,我想我已经回答了这两个问题。首先,为什么paintComponent() 被连续调用,它实际上并没有被连续调用。它在第一次显示 GUI 时被所有组件调用。当 Eclipse 窗口覆盖它然后又打开它时,它会被调用更多次。

第二个,它与Graphics2D 对象/事物的clipBounds 有关。我发现clipBounds 是如何为每个绘制调用而改变的,所以当我在paintComponent() 方法的开头设置剪辑时,图像会立即显示出来。 (顺便说一句,它看起来很棒!)。

有一个转折点:显示图像后,每次单击按钮都会对图像产生影响。不过,我还没有弄清楚到底是什么。它几乎看起来像是在旧图像上重新绘制相同的图像。

所以我必须弄清楚如何保留旧图像,但在适当的时候绘制新图像,并且只在glassPanel 上绘制/添加新图像。

更新:在单击每个按钮后立即调用 repaint() 会有所帮助。但是它仍然会导致图像有些闪烁,就像在按下按钮时添加了另一层一样,然后当用户松开时它会恢复正常。

【讨论】:

  • 考虑使用JPanel 而不是JComponent,因为this answer 中引用的两者之间存在细微差别:-)
  • 使用 JPanel 的问题是我不能做很多其他我想做的事情:检查它是否可见、自动双缓冲区、玻璃窗格等。
  • 你在开玩笑吗!!,Swing 默认使用双缓冲,在它的许多组件中(毫无疑问,JPanel 属于其中),看看Java Tutorials 对此有何评论:-)
  • 玻璃板怎么样?所以 setDoubleBuffer(true) 是没用的,除非我另有说明?另外,您是否认为问题可能在于缓冲图像被重绘,这样再次创建我自己的双缓冲区会很有用?
  • JPanelJComponent 的子类。无论如何,您不应该在paintComponent 中更改调用setDoubleBuffered,因为它可能会触发另一个重绘。您还应该避免玩clipBounds,它们是在绘制子组件之前由父容器设置的,以满足子组件的边界要求。查看Painting in AWT and Swing了解更多详情
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-26
  • 2022-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-03
  • 1970-01-01
相关资源
最近更新 更多