英文:
paintComponent called multiple times after frame initialization
问题
考虑这个示例:
@SuppressWarnings("serial")
public static void main(final String[] args) {
final var frame = new JFrame();
frame.setPreferredSize(new Dimension(150, 150));
frame.add(new JPanel() {
@Override protected void paintComponent(final Graphics g) {
super.paintComponent(g);
System.out.println("painting panel");
}
});
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SwingUtilities.invokeLater(() -> frame.setVisible(true));
}
这里有一个包含了被重写的 paintComponent 方法的 JPanel,每当调用这个方法时,它会简单地将一些内容打印到控制台。每次运行这个程序时,我会在控制台上看到 "painting panel" 这行文字被打印出来三次。
为什么 JPanel 的 paintComponent 方法会被调用多次而不是一次?有没有办法避免组件不必要的重绘?
英文:
Consider the example:
@SuppressWarnings("serial")
public static void main(final String[] args) {
final var frame = new JFrame();
frame.setPreferredSize(new Dimension(150, 150));
frame.add(new JPanel() {
@Override protected void paintComponent(final Graphics g) {
super.paintComponent(g);
System.out.println("painting panel");
}
});
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SwingUtilities.invokeLater(() -> frame.setVisible(true));
}
There is a JFrame with one JPanel with overridden paintComponent method, simply printing something to the console each time this method is called.
Every time I run this program, I can see "painting panel" line 3 times in my console.
Why is method paintComponent of JPanel called multiple times instead of once? Is there a way to avoid unnecessary repainting of components?
答案1
得分: 3
基本上,不会。但问题没有你想象的那么严重。
绘图请求受底层系统的图形驱动程序控制。有时应用程序可能只分配了少量视频内存,这种情况下系统会逐块地绘制窗口。
这意味着仅仅因为多次调用paintComponent
并不意味着组件正在进行完整的重绘;在方法被调用之前,Graphics对象可能已经设置了剪辑,只绘制窗口的一部分。paintComponent
方法不需要关注这一点,因为大多数Graphics操作如果确定不会影响当前剪辑范围之外的任何像素,它们会立即返回。这里的要点是,多次调用paintComponent
并不一定像你想象的那么浪费。
此外,正如Gilbert指出的,调用pack()
会使窗口"实现";也就是说,窗口会被分配一个实际的本机桌面窗口。(Java将其称为可显示。)这将触发绘制。显示窗口也可能触发绘制。移动窗口可能会触发绘制。将鼠标移到窗口上可能会触发绘制。
不能安全地假设绘制发生的频率。为了可靠地绘制,请假设paintComponent
方法可能会被调用任意次数。不要在绘制方法内部更改绘制基础的数据。
总的来说,没有办法控制绘制发生的时间。当然,如果程序调用了repaint
,你可以控制何时以及如何调用它。
英文:
Essentially, no. But it’s not as problematic as you think.
Painting requests are controlled by the underlying system’s graphics driver. Sometimes an application may be allocated only a small amount of video memory, in which case the system will paint a window one small piece at a time.
Which means that just because paintComponent is called multiple times doesn’t mean a full repaint of the component is taking place; before the method is called, the Graphics object may have a clip set to only paint part of the window. paintComponent methods don’t need to concern themselves with this, as most Graphics operations will return immediately if they can know they won’t affect any pixels outside of the current clip bounds. The point here is that multiple calls to paintComponent aren’t necessarily as wasteful as you think.
Also, as Gilbert pointed out, calling pack()
will “realize” the Window; that is, the Window gets assigned an actual native desktop window. (Java refers to this as becoming displayable.) That will trigger a paint. Showing the window may also trigger a paint. Moving it may trigger a paint. Moving the mouse over it may trigger a paint.
It is never safe to make assumptions about how often painting occurs. To paint reliably, assume your paintComponent method may be called any number of times. Never change the data which is the basis for painting from within a painting method.
In general, there is no way to control when painting happens. Of course, if your program calls repaint
, you do have control over how and when you call that.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论