为什么JFrame的输出与.paint方法不同,以及如何解决这个问题?

问题描述:

我试图设置一个系统,使用Swing呈现项目的快速列​​表,因为它的布局管理器完全适合我的情况。然而,在尝试使用文本包装时,我发现演示JFrame中显示的结果与调用.print/.paint(All)时得到的结果不同。为什么JFrame的输出与.paint方法不同,以及如何解决这个问题?

这是一个最小的,完整的,并且可验证例如:

import javax.imageio.ImageIO; 
import javax.swing.*; 
import java.awt.*; 
import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.IOException; 

public class TextWrapTest { 

    public static void main(String[] args) throws IOException { 
     final JPanel panel = new JPanel(new GridBagLayout()); 
     final GridBagConstraints gbc = new GridBagConstraints(); 
     gbc.gridy = 0; 
     gbc.weightx = 1; 
     gbc.fill = GridBagConstraints.HORIZONTAL; 

     panel.add(new JLabel("<html>This is a piece of text."), gbc); 
     gbc.gridy++; 
     panel.add(new JLabel("<html>This is a very long piece of text that needs to be wrapped in order to fit in the frame."), gbc); 
     gbc.gridy++; 
     panel.add(new JLabel("<html>Another piece of text."), gbc); 

     // Output the frame 
     final JFrame frame = new JFrame(); 
     frame.setUndecorated(true); 
     frame.setSize(200, 200); 
     frame.add(panel); 

     frame.setVisible(true); 

     // Output the image 
     final BufferedImage image = new BufferedImage(frame.getWidth(), frame.getHeight(), BufferedImage.TYPE_INT_RGB); 
     frame.paintAll(image.getGraphics()); 
     ImageIO.write(image, "png", new File("output.png")); 
    } 

} 

JFrame中:https://i.stack.imgur.com/zbTPz.png
output.png:https://i.stack.imgur.com/OcM4x.png

正如你所看到的,其结果显然是不同的,文本拒绝使用.getGraphics()

绘制使用GridBagConstraints,甚至尝试使用JTextArea的文本包装,而不是打包无济于事。

在这个问题上的任何帮助将不胜感激,因为我不知道如何让paint方法服从文字环绕。

此致,
伦斯Rikkerink

+0

首先要画到屏幕上,你应该避免调用'paintAll',坚持'printAll',因为这会删除所有的双缓冲​​和其他一些相关问题 – MadProgrammer

+0

你可以也是在与UI的竞争状态中变得实现并着色 – MadProgrammer

+0

@MadProgrammer这似乎是问题所在。添加'Thread.sleep(1000);'解决了他的问题 – XtremeBaumer

可能有许多原因,但是,我会建议避免paintpaintAll,因为他们调用API的双缓冲方面,可能会导致之前的延迟图像实际上被绘制到Graphics上下文中。相反,我建议使用printAll,因为它在绘画时会禁用双缓冲。

另一个问题可能是框架可能无法在屏幕上实现(或甚至没有完全绘制)。调用setVisible会调用很多后台工作,并会调用EDT回调以启动绘画过程。在这种情况下,我建议在UI显示后使用SwingUtilities.invokeLater块。

这会做一些事情,首先,我可以将任务放置在事件队列中,在事件队列创建完成后,但更重要的是,它会同步绘画,所以你不打架EDT试图在同一时间

SwingUtilities.invokeLater(new Runnable() { 
    @Override 
    public void run() { 
     final BufferedImage image = new BufferedImage(frame.getWidth(), frame.getHeight(), BufferedImage.TYPE_INT_RGB); 
     Graphics2D g2d = image.createGraphics(); 
     frame.paintAll(g2d); 
     g2d.dispose(); 
     ImageIO.write(image, "png", new File("output.png")); 
    } 
});